Gelukkig gebruik je PostgreSQL. De vensterfunctie generate_series() is je vriend.
Testcase
Gezien de volgende testtabel (die u had moeten verstrekken):
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';
Eén afspraak voor elke 7 minuten (plus 0 tot 7 minuten, willekeurig).
Basisoplossing
Deze query telt gebeurtenissen voor elk willekeurig tijdsinterval. 17 minuten in het voorbeeld:
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;
-
De query haalt minimale en maximale
tsvan de basistabel om het volledige tijdbereik te dekken. U kunt in plaats daarvan een willekeurig tijdbereik gebruiken. -
Geef elke tijdsinterval indien nodig.
-
Produceert één rij voor elke tijdslot. Als er tijdens dat interval geen gebeurtenis heeft plaatsgevonden, is de telling
0. -
Zorg ervoor dat u boven- en ondergrens . hanteert correct:
- Onverwachte resultaten van SQL-query met TUSSEN tijdstempels
-
De vensterfunctie
lead()heeft een vaak over het hoofd geziene functie:het kan een standaard bieden voor wanneer er geen leidende rij bestaat. Het verstrekken van'infinity'in het voorbeeld. Anders zou het laatste interval worden afgebroken met een bovengrensNULL.
Minimaal equivalent
De bovenstaande query gebruikt een CTE en lead() en uitgebreide syntaxis. Elegant en misschien gemakkelijker te begrijpen, maar een beetje duurder. Hier is een kortere, snellere, minimale versie:
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;
Voorbeeld voor 'elke 15 minuten in de afgelopen week'`
En formatteren met 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;
Nog steeds ORDER BY en GROUP BY op de onderliggende tijdstempel waarde , niet op de geformatteerde string. Dat is sneller en betrouwbaarder.
db<>viool hier
Gerelateerd antwoord met een lopende telling over het tijdsbestek:
- PostgreSQL:lopende telling van rijen voor een zoekopdracht 'per minuut'