PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Beste Möglichkeit, Datensätze in Rails + Postgres nach beliebigen Zeitintervallen zu zählen

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 Obergrenze NULL 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'