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
tsfrå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"