Próbujemy obliczyć najbardziej odpowiednią jednostkę miary dla listy substancji, których substancje podano w różnych (ale zgodnych) jednostkach objętości.
Tabela przeliczania jednostek
Tabela konwersji jednostek przechowuje różne jednostki i ich relacje:
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
Sortowanie według współczynnika pokazuje, że parent_id
łączy jednostkę potomną z jej liczbową wartością nadrzędną.
Tę tabelę można utworzyć w PostgreSQL przy użyciu:
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)
)
Powinien istnieć klucz obcy od parent_id
do id
.
Tabela substancji
Tabela substancji zawiera określone ilości substancji. Na przykład:
id unit label quantity
1 "microlitre" mercury 5
2 "millilitre" water 500
3 "centilitre" water 2
4 "microlitre" mercury 10
5 "millilitre" water 600
Tabela może przypominać:
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)
)
Problem
Jak utworzyłbyś zapytanie, które znajdzie pomiar reprezentujący sumę substancji przy użyciu jak najmniejszej liczby cyfr, które mają liczbę całkowitą (i opcjonalnie rzeczywisty składnik)?
Na przykład, jak byś zwrócił:
quantity unit label
15 microlitre mercury
112 centilitre water
Ale nie:
quantity unit label
15 microlitre mercury
1.12 litre water
Ponieważ 112 ma mniej rzeczywistych cyfr niż 1,12, a 112 jest mniejszy niż 1120. Jednak w niektórych sytuacjach użycie prawdziwych cyfr jest krótsze - na przykład 1,1 litra w porównaniu do 110 centylitrów.
Przeważnie mam problemy z wybraniem właściwej jednostki na podstawie relacji rekurencyjnej.
Kod źródłowy
Do tej pory mam (oczywiście nie działa):
-- Normalize the quantities
select
sum( coefficient * quantity ) AS kilolitres
from
unit_conversion uc,
substance s
where
uc.unit = s.unit
group by
s.label
Pomysły
Czy wymaga to użycia dziennika 10 do ustalenia liczby cyfr?
Ograniczenia
Nie wszystkie jednostki mają moc dziesięciu. Na przykład: http://unitsofmeasure.org/ucum-essence.xml
źródło
Odpowiedzi:
To wygląda brzydko:
ale wydaje się, że załatwia sprawę:
Tak naprawdę nie potrzebujesz relacji rodzic-dziecko w
unit_conversion
tabeli, ponieważ jednostki w tej samej rodzinie są naturalnie powiązane ze sobą według kolejnościcoefficient
, o ile zidentyfikujesz rodzinę.źródło
Myślę, że można to znacznie uprościć.
1. Zmodyfikuj
unit_conversion
tabelęLub, jeśli nie możesz zmodyfikować tabeli, po prostu dodaj kolumnę
exp10
„podstawa wykładnika 10”, która pokrywa się z liczbą cyfr do przesunięcia w systemie dziesiętnym:2. Funkcja zapisu
aby obliczyć liczbę pozycji do przesunięcia w lewo lub w prawo:
3. Zapytanie
Wyjaśnić:
f_shift_comma()
z góry.DISTINCT ON ()
iORDER BY
.COALESCE()
.-> Demo SQLfiddle .
źródło