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_event
tabela, trunca a data de criação para algum intervalo (alinha o created
timestamp com a granularidade desejada da série temporal), agrupa por esse intervalo de tempo e soma as revenue
colunas 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-09
e interval = '1 day'
.
Quero adicionar ao resultado a soma das receitas até aquela data. Então, se antes 2018-08-05
houvesse 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
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:O
JOIN
exclui linhas excedentes principais na subconsulta automaticamente.sum(sum(revenue)) OVER (ORDER BY date_trunc($4, created))
funciona porque, citando o manual :Exatamente o que você precisa.
Relacionado:
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:
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_ts
em vez deinterval_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_ts
para permitir aUSING
sintaxe mais curta - o que requer parênteses. Isso expõe apenas uma instância das colunas unidasinterval_ts
à consulta externa, portanto, o nome não qualificado ainda não é ambíguo.Não omita a
AS
palavra-chave para aliases de coluna (enquanto isso é bom para aliases de tabela).Se eu acertar, você pode simplesmente adicionar uma função de janela fora da sua consulta, como:
Outra opção é aplicar a função window diretamente e usar uma cláusula FILTER para
campaign_revenue