পরিমাপের একক রূপান্তর করুন


10

পদার্থের তালিকার জন্য পরিমাপের সর্বাধিক উপযুক্ত ইউনিট গণনা করতে দেখছি যেখানে পদার্থগুলি পৃথক (তবে সামঞ্জস্যপূর্ণ) ইউনিট খণ্ডে দেওয়া হয়।

ইউনিট রূপান্তর সারণী

ইউনিট রূপান্তর টেবিলটি বিভিন্ন ইউনিট সংরক্ষণ করে এবং কীভাবে সেই ইউনিটগুলি সম্পর্কিত:

id  unit          coefficient                 parent_id
36  "microlitre"  0.0000000010000000000000000 37
37  "millilitre"  0.0000010000000000000000000 5
 5  "centilitre"  0.0000100000000000000000000 18
18  "decilitre"   0.0001000000000000000000000 34
34  "litre"       0.0010000000000000000000000 19
19  "dekalitre"   0.0100000000000000000000000 29
29  "hectolitre"  0.1000000000000000000000000 33
33  "kilolitre"   1.0000000000000000000000000 35
35  "megalitre"   1000.0000000000000000000000 0

গুণফল দ্বারা বাছাই করা দেখায় যে parent_idচাইল্ড ইউনিটটিকে তার সংখ্যার চেয়ে আরও ভাল সংযুক্ত করে।

এই টেবিলটি পোস্টগ্রেএসকিউএল ব্যবহার করে তৈরি করা যেতে পারে:

CREATE TABLE unit_conversion (
  id serial NOT NULL, -- Primary key.
  unit text NOT NULL, -- Unit of measurement name.
  coefficient numeric(30,25) NOT NULL DEFAULT 0, -- Conversion value.
  parent_id integer NOT NULL DEFAULT 0, -- Relates units in order of increasing measurement volume.
  CONSTRAINT pk_unit_conversion PRIMARY KEY (id)
)

এখানে থেকে একটি বিদেশী কী থাকা parent_idউচিত id

পদার্থ সারণী

সাবস্ট্যান্স সারণীতে পদার্থের নির্দিষ্ট পরিমাণের তালিকা রয়েছে। উদাহরণ স্বরূপ:

 id  unit          label     quantity
 1   "microlitre"  mercury   5
 2   "millilitre"  water     500
 3   "centilitre"  water     2
 4   "microlitre"  mercury   10
 5   "millilitre"  water     600

টেবিল সাদৃশ্য হতে পারে:

CREATE TABLE substance (
  id bigserial NOT NULL, -- Uniquely identifies this row.
  unit text NOT NULL, -- Foreign key to unit conversion.
  label text NOT NULL, -- Name of the substance.
  quantity numeric( 10, 4 ) NOT NULL, -- Amount of the substance.
  CONSTRAINT pk_substance PRIMARY KEY (id)
)

সমস্যা

আপনি কীভাবে এমন একটি ক্যোয়ারী তৈরি করবেন যা পুরো সংখ্যার (এবং allyচ্ছিকভাবে বাস্তব উপাদান) কয়েকটি সংখ্যক অঙ্ক ব্যবহার করে পদার্থের যোগফলের প্রতিনিধিত্ব করতে একটি পরিমাপ খুঁজে পায়?

উদাহরণস্বরূপ, আপনি কীভাবে ফিরে আসবেন:

  quantity  unit        label
        15  microlitre  mercury 
       112  centilitre  water

কিন্তু না:

  quantity  unit        label
        15  microlitre  mercury 
      1.12  litre       water

কারণ 112 এর তুলনায় 1.12 এর চেয়ে কম কম সংখ্যক রয়েছে এবং 112 1120 এর চেয়ে ছোট Yet তবুও কিছু পরিস্থিতিতে আসল অঙ্কগুলি ব্যবহার করা সংক্ষিপ্ত - যেমন ১.১ লিটার বনাম ১১০ সেন্টিমিটার।

বেশিরভাগ ক্ষেত্রে, পুনরাবৃত্তির সম্পর্কের ভিত্তিতে সঠিক ইউনিট বাছাই করতে আমার সমস্যা হচ্ছে।

সোর্স কোড

এখন পর্যন্ত আমার কাছে (স্পষ্টত অ-কর্মহীন) রয়েছে:

-- Normalize the quantities
select
  sum( coefficient * quantity ) AS kilolitres
from
  unit_conversion uc,
  substance s
where
  uc.unit = s.unit
group by
  s.label

ধারনা

অঙ্কের সংখ্যা নির্ধারণ করতে এর জন্য লগ 10 ব্যবহার করা দরকার ?

সীমাবদ্ধতাসমূহ

ইউনিট দশটি ক্ষমতায় নেই। উদাহরণস্বরূপ: http://unitsofmeasure.org/ucum-es উপস্থিত.xml


3
@ মুস্তাকসিও আমার আগের স্থানে খুব একই প্রডাকশন সিস্টেমে ঠিক একই সমস্যা ছিল। সেখানে আমাদের একটি খাদ্য বিতরণ রান্নাঘরে ব্যবহৃত পরিমাণ গণনা করতে হয়েছিল।
dezso

2
আমি কমপক্ষে দুটি স্তরের পুনরাবৃত্ত সিটিই মনে করি। আমি মনে করি আমি প্রথমে ক্ষুদ্রতম ইউনিটের সাথে অঙ্কগুলি গণনা করেছি যা প্রদত্ত পদার্থের জন্য তালিকায় পরিণত হয়েছিল এবং তারপরে এটিকে শূন্যের পূর্ণসংখ্যার অংশ না থাকা এখনও সবচেয়ে বড় ইউনিটে রূপান্তরিত করে।
ডিজেসো

1
সমস্ত ইউনিট কি 10 এর ক্ষমতার সাথে রূপান্তরিত হয়? আপনার ইউনিট তালিকা সম্পূর্ণ?
এরউইন ব্র্যান্ডসটেটার

উত্তর:


2

এটি দেখতে কুৎসিত দেখাচ্ছে:

  with uu(unit, coefficient, u_ord) as (
    select
     unit, 
     coefficient,
     case 
      when log(u.coefficient) < 0 
      then floor (log(u.coefficient)) 
      else ceil(log(u.coefficient)) 
     end u_ord
    from
     unit_conversion u 
  ),
  norm (label, norm_qty) as (
   select
    s.label,
    sum( uc.coefficient * s.quantity ) AS norm_qty
  from
    unit_conversion uc,
    substance s
  where
    uc.unit = s.unit
  group by
    s.label
  ),
  norm_ord (label, norm_qty, log, ord) as (
   select 
    label,
    norm_qty, 
    log(t.norm_qty) as log,
    case 
     when log(t.norm_qty) < 0 
     then floor(log(t.norm_qty)) 
     else ceil(log(t.norm_qty)) 
    end ord
   from norm t
  )
  select
   norm_ord.label,
   norm_ord.norm_qty,
   norm_ord.norm_qty / uu.coefficient val,
   uu.unit
  from 
   norm_ord,
   uu where uu.u_ord = 
     (select max(uu.u_ord) 
      from uu 
      where mod(norm_ord.norm_qty , uu.coefficient) = 0);

তবে কৌশলটি মনে হচ্ছে:

|   LABEL | NORM_QTY | VAL |       UNIT |
-----------------------------------------
| mercury |   1.5e-8 |  15 | microlitre |
|   water |  0.00112 | 112 | centilitre |

আপনার unit_conversionটেবিলে পিতামাতৃ-সন্তানের সম্পর্কের সত্যই প্রয়োজন নেই , কারণ একই পরিবারে ইউনিটগুলি স্বাভাবিকভাবে একে অপরের সাথে সম্পর্কিত হয় coefficientযতক্ষণ না আপনি পরিবারটি সনাক্ত করেন।


2

আমি মনে করি, এটি অনেকাংশে সরল করা যেতে পারে।

1. unit_conversionটেবিল পরিবর্তন করুন

অথবা, আপনি যদি সারণিটি সংশোধন করতে না পারেন, তবে কেবলমাত্র exp10"এক্সপোনেন্ট বেস 10" এর জন্য কলাম যুক্ত করুন , যা দশমিক সিস্টেমে শিফট করার জন্য সংখ্যার সংখ্যার সাথে মিলে যায়:

CREATE TABLE unit_conversion(
   unit text PRIMARY KEY
  ,exp10 int
);

INSERT INTO unit_conversion VALUES
     ('microlitre', 0)
    ,('millilitre', 3)
    ,('centilitre', 4)
    ,('litre',      6)
    ,('hectolitre', 8)
    ,('kilolitre',  9)
    ,('megalitre',  12)
    ,('decilitre',  5);

2. ফাংশন লিখুন

বাম বা ডানে স্থানান্তরিত করতে পজিশনের সংখ্যা গণনা করতে:

CREATE OR REPLACE FUNCTION f_shift_comma(n numeric)
  RETURNS int LANGUAGE SQL IMMUTABLE AS
$$
SELECT CASE WHEN ($1 % 1) = 0 THEN                    -- no fractional digits
          CASE WHEN ($1 % 10) = 0 THEN 0              -- no trailing 0, don't shift
          ELSE length(rtrim(trunc($1, 0)::text, '0')) -- trunc() because numeric can be 1.0
                   - length(trunc($1, 0)::text)       -- trailing 0, shift right .. negative
          END
       ELSE                                           -- fractional digits
          length(rtrim(($1 % 1)::text, '0')) - 2      -- shift left .. positive
       END
$$;

3. প্রশ্ন

SELECT DISTINCT ON (substance_id)
       s.substance_id, s.label, s.quantity, s.unit
      ,COALESCE(s.quantity * 10^(u1.exp10 - u2.exp10)::numeric
              , s.quantity)::float8 AS norm_quantity
      ,COALESCE(u2.unit, s.unit) AS norm_unit
FROM   substance s 
JOIN   unit_conversion u1 USING (unit)
LEFT   JOIN unit_conversion u2 ON f_shift_comma(s.quantity) <> 0
                              AND @(u2.exp10 - (u1.exp10 - f_shift_comma(s.quantity))) < 2
                              -- since maximum gap between exp10 in unit table = 3
                              -- adapt to ceil(to max_gap / 2) if you have bigger gaps
ORDER  BY s.substance_id
     , @(u2.exp10 - (u1.exp10 - f_shift_comma(s.quantity))) -- closest unit first
     , u2.exp10    -- smaller unit first to avoid point for ties.

ব্যাখ্যা করা:

  • পদার্থ এবং ইউনিট টেবিলগুলিতে যোগদান করুন।
  • f_shift_comma()উপরের দিক থেকে ফাংশনটি সহ শিফ্ট করার জন্য আদর্শ সংখ্যাগুলির অবস্থান গণনা করুন ।
  • সর্বোত্তমটির কাছাকাছি ইউনিটগুলি খুঁজে পেতে দ্বিতীয় বার ইউনিট টেবিলের সাথে বাম যোগদান করুন।
  • DISTINCT ON ()এবং সঙ্গে সবচেয়ে নিকটতম ইউনিট চয়ন করুন ORDER BY
  • যদি আরও ভাল ইউনিট না পাওয়া যায় তবে আমাদের যা ছিল তা পিছিয়ে পড়ুন COALESCE()
  • এটি সমস্ত কোণার কেসগুলি কভার করে এবং বেশ দ্রুত হওয়া উচিত ।

-> এসকিউএলফিডাল ডেমো।


1
@ ডেভ জারভিস: এবং সেখানে আমি ভেবেছিলাম যে আমি সমস্ত কিছু কভার করে রেখেছি ... অন্যথায় সাবধানতার সাথে রচনা করা প্রশ্নে এই বিবরণটি সত্যই সহায়ক হতে পারে।
এরউইন ব্র্যান্ডসেটেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.