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 / 322527
Accepted
Camila326
Camila326
Asked: 2023-01-25 17:28:29 +0800 CST2023-01-25 17:28:29 +0800 CST 2023-01-25 17:28:29 +0800 CST

Como consultar o par mais recente de cada par de dados exclusivo?

  • 772

Há uma tabela onde estão as taxas de câmbio.

fx_rates
---
id
buy_curr_code
sell_curr_code
rate
inserted_at

Uma tabela é atualizada com frequência desconhecida: às vezes diariamente, às vezes várias vezes ao dia, às vezes uma vez em vários dias. O mesmo vale para as moedas: algumas podem ser atualizadas desta vez, outras no outro.

E os dados nunca são excluídos da tabela, mas apenas inseridos ou atualizados.

Como consultarei as taxas de câmbio mais recentes e atualizadas dele? Ou seja, uma única taxa de câmbio por cada buy - sell currency_codepar único.

Devo usar distincte se sim, como?

postgresql
  • 3 3 respostas
  • 291 Views

3 respostas

  • Voted
  1. Best Answer
    Laurenz Albe
    2023-01-25T22:30:55+08:002023-01-25T22:30:55+08:00

    A resposta de Erwin está correta, mas a consulta ficará cada vez mais lenta conforme a tabela cresce. Então deixe-me sugerir um modelo de dados diferente:

    Adicione uma coluna activedo tipo booleanTRUE para a entrada mais recente por (buy_curr_code, sell_curr_code).

    Então você adicionaria um índice:

    CREATE UNIQUE INDEX ON fx_rates (buy_curr_code, sell_curr_code) WHERE active;
    

    Para inserir uma nova linha, você executaria esta transação:

    BEGIN;
    
    UPDATE fx_rates
    SET active = FALSE
    WHERE buy_curr_code = 42 AND sell_curr_code = 101
    RETURNING inserted_at;
    
    /* here I would add a test in my code if the "inserted_at" is more
       recent than "current_timestamp".  If yes, I would have the transaction
       fail, because there is already a more recent entry.
       This should take care of race conditions. */
    
    INSERT INTO fy_rates (buy_curr_code, sell_curr_code, rate, inserted_at, active)
    VALUES (42, 101, 2.71828, current_timestamp, TRUE);
    
    COMMIT;
    

    O bom dessa solução é que agora é fácil consultar as entradas ativas e o desempenho dessa consulta será independente do tamanho da tabela.

    Para limpar os dados, você pode particionar a tabela por lista activee subparticionar a partição FALSE por intervalo de tempo.

    • 4
  2. Erwin Brandstetter
    2023-01-25T19:15:04+08:002023-01-25T19:15:04+08:00

    DISTINCT ONé uma das maneiras mais simples:

    SELECT DISTINCT ON (buy_curr_code, sell_curr_code) *
    FROM   fx_rates
    ORDER  BY buy_curr_code, sell_curr_code, inserted_at DESC;
    

    Tenha um índice (buy_curr_code, sell_curr_code, inserted_at DESC)para tornar isso rápido. Ver:

    • Selecione a primeira linha em cada grupo GROUP BY?

    Se houver muitas linhas por (buy_curr_code, sell_curr_code)- o que parece provável - outras técnicas de consulta serão mais rápidas. As especificidades dependem de detalhes não revelados. Ver:

    • Otimize a consulta GROUP BY para recuperar a última linha por usuário

    Melhor design

    Se alterar o design do banco de dados for uma opção, eu consideraria uma tabela adicional com uma única entrada por conversão. Como:

    CREATE TABLE current_rate (
      exchange_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY
    , buy_curr_code   int NOT NULL  -- or whatever type 
    , sell_curr_code  int NOT NULL
    , rate            numeric NOT NULL
    , inserted_at     timestamptz NOT NULL DEFAULT now()
    , UNIQUE (buy_curr_code, sell_curr_code)
    );
    

    E dispara ON INSERTe ON UPDATEinsere uma nova entrada de "log" na tabela fx_rates. Todas as novas entradas são atualizações para current_rate. Somente o gatilho grava na tabela fx_rates. (O gatilho pode executar verificações adicionais.)

    INSERTacionar:

    -- function
    CREATE OR REPLACE FUNCTION trg_current_rate_insbef()
      RETURNS trigger
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       INSERT INTO fx_rate (exchange_id, rate, inserted_at)
       VALUES (NEW.exchange_id, NEW.rate, NEW.inserted_at);
            
       RETURN NEW;
    END
    $func$;
    
    -- trigger
    CREATE TRIGGER current_rate_insbef
    BEFORE INSERT ON current_rate
    FOR EACH ROW EXECUTE FUNCTION trg_current_rate_insbef();
    

    Demonstração completa neste violino .

    Então o conteúdo de current_rateé sempre o resultado pronto que você tentou gerar.

    Por que?

    Cada abordagem tem prós e contras. O único requisito declarado é a lista de taxas atuais. Minha solução fornece isso SELECT * FROM current_rate- o mais simples e rápido possível. Adicionar uma nova taxa é um único arquivo UPDATE. Armazenamento: fx_ratesé ordem de grandeza maior que current_rate. Não precisamos de nenhum índice naquela grande mesa. É efetivamente INSERTapenas, portanto, nenhuma tabela e índice incham. Podemos tornar a tabela grande ainda menor adicionando uma IDENTITYcoluna inteira como substituto PK para current_rate, e apenas escrever este ID de 4 bytes para fx_rates. Na verdade, nada em sua pergunta diz que ainda precisamos, fx_ratesuma vez que temos current_rates. (Mas eu esperaria que houvesse propósitos adicionais.)

    A única sofisticação moderada é o gatilho, que também é muito simples. Portanto, a menos que você tenha outros requisitos, o design sugerido é mais simples, rápido, menor e mais confiável.

    • 3
  3. J.D.
    2023-01-26T05:20:41+08:002023-01-26T05:20:41+08:00

    Uma terceira alternativa que funciona muito bem em termos de desempenho e não exige que você altere o esquema da tabela, é usar uma função de janela como ROW_NUMBER(). Isso permite que você enumere as linhas dentro de cada grupo de buy_curr_code, sell_curr_codepares e, em seguida, selecione apenas a última linha por grupo da seguinte forma:

    WITH ExchangeRatesSorted AS
    (
        SELECT 
            id,
            buy_curr_code,
            sell_curr_code,
            rate,
            inserted_at,
            ROW_NUMBER() OVER (PARTITION BY buy_curr_code, sell_curr_code ORDER BY inserted_at DESC, id DESC) AS InsertedSortId
        FROM fx_rates
    )
    
    SELECT 
        id,
        buy_curr_code,
        sell_curr_code,
        rate,
        inserted_at
    FROM ExchangeRatesSorted
    WHERE InsertedSortId = 1
    

    Você provavelmente deseja um índice (buy_curr_code, sell_curr_code, inserted_at, id)para tornar isso mais eficiente.

    A razão idé adicionada ao final da ORDER BYcláusula na ROW_NUMBER()expressão é para a chance de duas linhas serem inseridas exatamente ao mesmo inserted_attempo, então aquela com a última id(idealmente a linha que realmente veio por último) é o desempate, para garantir que a ORDER BYcláusula seja determinística.


    As funções de janela são ferramentas muito úteis para fazer cálculos e manipular os dados.

    • 1

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

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 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

    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
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • 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
    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