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 / 214577
Accepted
AlexGordon
AlexGordon
Asked: 2018-08-11 01:48:06 +0800 CST2018-08-11 01:48:06 +0800 CST 2018-08-11 01:48:06 +0800 CST

Adicionar soma cumulativa à consulta de série temporal PostgreSQL 9.5

  • 772

Eu escrevi a consulta que me fornece uma série temporal em algum intervalo de datas e intervalo que mostra a receita para cada intervalo de tempo:

SELECT
    interval_date,
    coalesce(campaign_revenue,0) AS campaign_revenue,
FROM
    -- generate_series helps fill the empty gaps in the following JOIN
    generate_series(
        $2::timestamp,
        $3::timestamp,
        $4) AS interval_date -- could be '1 day', '1 hour' or '1 minute'.
LEFT OUTER JOIN
    -- This SELECT gets all timeseries rows that have data
    (SELECT
        date_trunc($4, s.created) AS interval,
        SUM(s.revenue) campaign_revenue
    FROM
        sale_event AS s
    WHERE
        s.campaignid = $1 AND s.created BETWEEN $2 AND $3 AND s.event_type = 'session_closed'
    GROUP BY
        interval) results
ON
    (results.interval = interval_date);

A consulta pega todas as linhas da sale_eventtabela, trunca a data de criação para algum intervalo (alinha o createdtimestamp com a granularidade desejada da série temporal), agrupa por esse intervalo de tempo e soma as revenuecolunas nas linhas onde event_typeé session_closed.

Isso funciona muito bem e me dá a receita no intervalo especificado. O resultado pode se parecer com:

interval_date   |   campaign_revenue
------------------------------------
 2018-08-05     |   0.0
 2018-08-06     |   1.5
 2018-08-07     |   0.0
 2018-08-08     |   0.5
 2018-08-09     |   1.0

Quando o intervalo fornecido for 2018-08-05 - 2018-08-09e interval = '1 day'.

Quero adicionar ao resultado a soma das receitas até aquela data. Então, se antes 2018-08-05houvesse uma receita total de 10.0, o resultado seria:

interval_date   |   campaign_revenue   |   total_campaign_revenue
-----------------------------------------------------------------
 2018-08-05     |   0.0                |   10.0
 2018-08-06     |   1.5                |   11.5
 2018-08-07     |   0.0                |   11.5
 2018-08-08     |   0.5                |   12.0
 2018-08-09     |   1.0                |   13.0
postgresql aggregate
  • 2 2 respostas
  • 5940 Views

2 respostas

  • Voted
  1. Erwin Brandstetter
    2018-08-11T06:45:23+08:002018-08-11T06:45:23+08:00

    Normalmente, é mais rápido ler todas as linhas relevantes da tabela subjacente em uma única verificação.
    Você pode executar uma função de janela sobre uma função agregada no mesmo arquivo SELECT.

    Teste com EXPLAIN (ANALYZE, TIMING OFF)para verificar se é realmente mais rápido:

    SELECT interval_ts
         , coalesce(revenue      , 0) AS campaign_revenue
         , coalesce(total_revenue, 0) AS total_campaign_revenue    
    FROM   generate_series($2::timestamp, $3::timestamp, $4) AS interval_ts
    LEFT   JOIN (
       SELECT date_trunc($4, created) AS interval_ts
            , sum(revenue)                                              AS revenue
            , sum(sum(revenue)) OVER (ORDER BY date_trunc($4, created)) AS total_running
       FROM   sale_event AS s
       WHERE  campaignid = $1
       AND    created <= $3                   -- read all relevant rows in one scan
       AND    event_type = 'session_closed'
       GROUP  BY date_trunc($4, created)
       ) results USING (interval_ts);
    

    O JOINexclui linhas excedentes principais na subconsulta automaticamente.

    sum(sum(revenue)) OVER (ORDER BY date_trunc($4, created))funciona porque, citando o manual :

    A opção de enquadramento padrão é RANGE UNBOUNDED PRECEDING, que é a mesma que RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Com ORDER BY, isso define o quadro para que todas as linhas da partição sejam iniciadas até o último ORDER BYpar da linha atual.

    Exatamente o que você precisa.

    Relacionado:

    • Calculando a soma cumulativa no PostgreSQL
    • PostgreSQL: executando contagem de linhas para uma consulta 'por minuto'
    • Como obtenho a agregação de uma função de janela no Postgres?

    Ponto fraco restante: o total está ausente para intervalos sem receita. Se isso não for aceitável, podemos usar esta técnica para corrigir:

    • Carregue uma longa sequência de valores ausentes com o Postgres
    SELECT interval_ts, campaign_revenue, total_revenue
         , coalesce(first_value(total_revenue) OVER (PARTITION BY grp ORDER BY interval_ts), 0) AS total_campaign_revenue    
    FROM  (
       SELECT interval_ts
            , coalesce(revenue, 0) AS campaign_revenue
            , total_revenue
            , count(total_revenue) OVER (ORDER BY interval_ts) AS grp
       FROM  (
          SELECT interval_ts
               , coalesce(revenue, 0) AS campaign_revenue
               , count(total_revenue) OVER (ORDER BY interval_ts) AS grp
          FROM   generate_series($2::timestamp, $3::timestamp, $4) AS interval_ts
          LEFT   JOIN (
             SELECT date_trunc($4, created) AS interval_ts
                  , sum(revenue) AS revenue
                  , sum(sum(revenue)) OVER (ORDER BY date_trunc($4, created)) AS total_running
             FROM   sale_event AS s
             WHERE  campaignid = $1
             AND    created <= $3                   -- read all relevant rows in one scan
             AND    event_type = 'session_closed'
             GROUP  BY date_trunc($4, created)
             ) results USING (interval_ts)
          ) sub1
       ) sub2;
    

    Com a sobrecarga adicional, não tenho certeza se pode competir. Ainda pode se sua seleção for pequena e a mesa for grande.

    Notas menores:

    • Você não precisa de parênteses em torno das condições de junção.

    • Não chame seu carimbo de data/hora de "data". Isso é enganoso. Eu uso interval_tsem vez de interval_date.

    • Prefiro não usar a palavra- chave SQL interval como alias de coluna - mesmo que isso seja permitido no Postgres.

    • Trabalhar com o mesmo alias de coluna interval_tspara permitir a USINGsintaxe mais curta - o que requer parênteses. Isso expõe apenas uma instância das colunas unidas interval_tsà consulta externa, portanto, o nome não qualificado ainda não é ambíguo.

    • Não omita a ASpalavra-chave para aliases de coluna (enquanto isso é bom para aliases de tabela).

    • 4
  2. Best Answer
    Lennart - Slava Ukraini
    2018-08-11T02:12:08+08:002018-08-11T02:12:08+08:00

    Se eu acertar, você pode simplesmente adicionar uma função de janela fora da sua consulta, como:

    SELECT interval_date, campaign_revenue
         , SUM(campaign_revenue) OVER (ORDER BY interval_date) 
          + (SELECT SUM(revenue) 
             FROM sale_event
             WHERE s.campaignid = $1
               AND s.created < $2
               AND s.event_type = 'session_closed') as total_campaign_revenue
    FROM (
        SELECT interval_date
             , coalesce(campaign_revenue,0) AS campaign_revenue
        FROM
            -- generate_series helps fill the empty gaps in the following JOIN
            ...
            interval) results
        ON (results.interval = interval_date)
    );
    

    Outra opção é aplicar a função window diretamente e usar uma cláusula FILTER paracampaign_revenue

    • 3

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