Felizmente, você está usando o PostgreSQL. A função de janela
generate_series()
é seu amigo. Caso de teste
Dada a seguinte tabela de teste (que você deveria ter fornecido):
CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
, timestamp '2018-05-08'
, interval '7 min') + random() * interval '7 min';
Um evento a cada 7 minutos (mais 0 a 7 minutos, aleatoriamente).
Solução básica
Essa consulta conta eventos para qualquer intervalo de tempo arbitrário. 17 minutos no exemplo:
WITH grid AS (
SELECT start_time
, lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
FROM (
SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
FROM event
) sub
)
SELECT start_time, count(e.ts) AS events
FROM grid g
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.end_time
GROUP BY start_time
ORDER BY start_time;
-
A consulta recuperats
mínimo e máximo da tabela base para cobrir todo o intervalo de tempo. Você pode usar um intervalo de tempo arbitrário.
-
Forneça qualquer intervalo de tempo como necessário.
-
Produz uma linha para cada intervalo de tempo. Se nenhum evento aconteceu durante esse intervalo, a contagem é0
.
-
Certifique-se de lidar com limite superior e inferior corretamente:
- Resultados inesperados da consulta SQL com BETWEEN timestamps
-
A função de janelalead()
tem um recurso muitas vezes esquecido:ele pode fornecer um padrão para quando não existir nenhuma linha inicial. Fornecendo'infinity'
no exemplo. Caso contrário, o último intervalo seria cortado com um limite superiorNULL
.
Equivalente mínimo
A consulta acima usa um CTE e
lead()
e sintaxe detalhada. Elegante e talvez mais fácil de entender, mas um pouco mais caro. Aqui está uma versão mais curta, rápida e mínima:SELECT start_time, count(e.ts) AS events
FROM (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '17 min'
GROUP BY 1
ORDER BY 1;
Exemplo para "a cada 15 minutos na última semana"`
E formatação com
to_char()
. SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM generate_series(date_trunc('day', localtimestamp - interval '7 days')
, localtimestamp
, interval '15 min') g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '15 min'
GROUP BY start_time
ORDER BY start_time;
Ainda
ORDER BY
e GROUP BY
no carimbo de data/hora subjacente valor , não na string formatada. Isso é mais rápido e confiável. db<>mexa aqui
Resposta relacionada produzindo uma contagem em execução ao longo do tempo:
- PostgreSQL:contagem de linhas em execução para uma consulta 'por minuto'