AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 45246
Accepted
Dave Jarvis
Dave Jarvis
Asked: 2013-06-26 15:55:48 +0800 CST2013-06-26 15:55:48 +0800 CST 2013-06-26 15:55:48 +0800 CST

Converter unidades de medida

  • 772

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_idvincula 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_idpara 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

postgresql postgresql-9.1
  • 2 2 respostas
  • 4961 Views

2 respostas

  • Voted
  1. Best Answer
    mustaccio
    2013-06-27T17:24:59+08:002013-06-27T17:24:59+08:00

    Isso parece feio:

      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);
    

    mas parece fazer o truque:

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

    Você realmente não precisa do relacionamento pai-filho na unit_conversiontabela, porque as unidades na mesma família são naturalmente relacionadas umas às outras pela ordem de coefficient, desde que você tenha a família identificada.

    • 2
  2. Erwin Brandstetter
    2013-07-25T17:21:46+08:002013-07-25T17:21:46+08:00

    Eu acho que isso pode ser amplamente simplificado.

    1. Modificar unit_conversiontabela

    Ou, se não for possível modificar a tabela, basta adicionar a coluna exp10para "expoente base 10", que coincide com o número de dígitos a deslocar no sistema decimal:

    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. Função de gravação

    para calcular o número de posições para deslocar para a esquerda ou para a direita:

    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. Consulta

    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.
    

    Explique:

    • JOIN tabelas de substâncias e unidades.
    • Calcule o número ideal de posições para mudar com a função f_shift_comma()de cima.
    • LEFT JOIN à tabela de unidades uma segunda vez para encontrar unidades próximas do ideal.
    • Escolha a unidade mais próxima com DISTINCT ON ()e ORDER BY.
    • Se nenhuma unidade melhor for encontrada, volte para o que tínhamos com COALESCE().
    • Isso deve cobrir todos os casos extremos e ser bem rápido .

    -> Demonstração do SQLfiddle .

    • 2

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Conceder acesso a todas as tabelas para um usuário

    • 5 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve