Glücklicherweise verwenden Sie PostgreSQL. Die Fensterfunktion generate_series()
ist dein Freund.
Testfall
Angesichts der folgenden Testtabelle (welche Sie hätten bereitstellen sollen):
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';
Ein Ereignis alle 7 Minuten (plus 0 bis 7 Minuten, zufällig).
Basislösung
Diese Abfrage zählt Ereignisse für ein beliebiges Zeitintervall. 17 Minuten im Beispiel:
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;
-
Die Abfrage ruft minimales und maximales
ts
ab aus der Basistabelle, um den gesamten Zeitbereich abzudecken. Sie können stattdessen einen beliebigen Zeitraum verwenden. -
Geben Sie alle an Zeitintervall nach Bedarf.
-
Erzeugt eine Zeile für jeden Zeitfenster. Wenn während dieses Intervalls kein Ereignis aufgetreten ist, ist der Zähler
0
. -
Achten Sie darauf, Ober- und Untergrenze zu handhaben richtig:
- Unerwartete Ergebnisse einer SQL-Abfrage mit ZWISCHEN Zeitstempeln
-
Die Fensterfunktion
lead()
hat eine oft übersehene Funktion:Sie kann einen Standard dafür bereitstellen, wenn keine führende Zeile vorhanden ist. Bereitstellen von'infinity'
im Beispiel. Sonst würde das letzte Intervall mit einer ObergrenzeNULL
abgeschnitten .
Minimales Äquivalent
Die obige Abfrage verwendet einen CTE und lead()
und ausführliche Syntax. Elegant und vielleicht einfacher zu verstehen, aber etwas teurer. Hier ist eine kürzere, schnellere Minimalversion:
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;
Beispiel für "alle 15 Minuten in der letzten Woche"`
Und die Formatierung mit 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;
Immer noch ORDER BY
und GROUP BY
auf dem zugrunde liegenden Zeitstempel-Wert , nicht auf die formatierte Zeichenfolge. Das ist schneller und zuverlässiger.
db<>hier fummeln
Verwandte Antwort, die eine laufende Zählung erzeugt über den Zeitraum:
- PostgreSQL:Laufende Anzahl von Zeilen für eine Abfrage 'pro Minute'