Procurando calcular a unidade de medida mais adequada para uma lista de substâncias em que as substâncias são dadas em unidades de volume diferentes (mas compatíveis).
Tabela de conversão de unidades
A tabela de conversão de unidades armazena várias unidades e como essas unidades se relacionam:
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
A classificação pelo coeficiente mostra que parent_id
vincula uma unidade filho ao seu superior numérico.
Esta tabela pode ser criada no PostgreSQL usando:
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)
)
Deve haver uma chave estrangeira de parent_id
para id
.
Tabela de substâncias
A Tabela de Substâncias lista quantidades específicas de substâncias. Por exemplo:
id unit label quantity
1 "microlitre" mercury 5
2 "millilitre" water 500
3 "centilitre" water 2
4 "microlitre" mercury 10
5 "millilitre" water 600
A tabela pode se parecer com:
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)
)
Problema
Como você criaria uma consulta que encontra uma medida para representar a soma das substâncias usando o menor número de dígitos que possui um número inteiro (e, opcionalmente, componente real)?
Por exemplo, como você retornaria:
quantity unit label
15 microlitre mercury
112 centilitre water
Mas não:
quantity unit label
15 microlitre mercury
1.12 litre water
Porque 112 tem menos dígitos reais do que 1,12 e 112 é menor do que 1120. No entanto, em certas situações, o uso de dígitos reais é menor - como 1,1 litros versus 110 centilitros.
Principalmente, estou tendo problemas para escolher a unidade correta com base na relação recursiva.
Código fonte
Até agora eu tenho (obviamente não trabalho):
-- Normalize the quantities
select
sum( coefficient * quantity ) AS kilolitres
from
unit_conversion uc,
substance s
where
uc.unit = s.unit
group by
s.label
Ideias
Isso requer o uso de log 10 para determinar o número de dígitos?
Restrições
As unidades não estão todas em potências de dez. Por exemplo: http://unitsofmeasure.org/ucum-essence.xml
Isso parece feio:
mas parece fazer o truque:
Você realmente não precisa do relacionamento pai-filho na
unit_conversion
tabela, porque as unidades na mesma família são naturalmente relacionadas umas às outras pela ordem decoefficient
, desde que você tenha a família identificada.Eu acho que isso pode ser amplamente simplificado.
1. Modificar
unit_conversion
tabelaOu, se não for possível modificar a tabela, basta adicionar a coluna
exp10
para "expoente base 10", que coincide com o número de dígitos a deslocar no sistema decimal:2. Função de gravação
para calcular o número de posições para deslocar para a esquerda ou para a direita:
3. Consulta
Explique:
f_shift_comma()
de cima.DISTINCT ON ()
eORDER BY
.COALESCE()
.-> Demonstração do SQLfiddle .