Lyckligtvis använder du PostgreSQL. Fönsterfunktionen generate_series()
är din vän.
Testfall
Med tanke på följande testtabell (som du borde ha tillhandahållit):
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';
Ett evenemang var 7:e minut (plus 0 till 7 minuter, slumpmässigt).
Grundläggande lösning
Denna fråga räknar händelser för godtyckliga tidsintervall. 17 minuter i exemplet:
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;
-
Frågan hämtar minsta och högsta
ts
från bastabellen för att täcka hela tidsintervallet. Du kan använda ett godtyckligt tidsintervall istället. -
Ange alla tidsintervall efter behov.
-
Ger en rad för varje tidslucka. Om ingen händelse inträffade under det intervallet är räkningen
0
. -
Var noga med att hantera övre och nedre gräns korrekt:
- Oväntade resultat från SQL-fråga med BETWEEN tidsstämplar
-
Fönsterfunktionen
lead()
har en ofta förbisedd funktion:den kan tillhandahålla en standard för när det inte finns någon inledande rad. Tillhandahåller'infinity'
i exemplet. Annars skulle det sista intervallet skäras av med en övre gränsNULL
.
Minsta motsvarighet
Ovanstående fråga använder en CTE och lead()
och utförlig syntax. Elegant och kanske lättare att förstå, men lite dyrare. Här är en kortare, snabbare, minimal version:
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;
Exempel för "var 15:e minut under den senaste veckan"`
Och formatering med 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;
Fortfarande ORDER BY
och GROUP BY
på den underliggande tidsstämpeln värde , inte på den formaterade strängen. Det är snabbare och mer pålitligt.
db<>spela här
Relaterat svar som ger en löpande räkning över tidsramen:
- PostgreSQL:löpande antal rader för en fråga "per minut"