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

PostgreSQL:Laufende Anzahl von Zeilen für eine Abfrage „pro Minute“

Nur Minuten mit Aktivität zurückgeben

Am kürzesten

SELECT DISTINCT
       date_trunc('minute', "when") AS minute
     , count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM   mytable
ORDER  BY 1;

Verwenden Sie date_trunc() , gibt es genau das zurück, was Sie brauchen.

Geben Sie id nicht ein in der Abfrage, da Sie GROUP BY möchten winzige Scheiben.

count() wird typischerweise als einfache Aggregatfunktion verwendet. Anhängen eines OVER -Klausel macht es zu einer Fensterfunktion. Lassen Sie PARTITION BY weg in der Fensterdefinition - Sie möchten eine laufende Zählung über alle Zeilen . Standardmäßig zählt dies von der ersten Zeile bis zum letzten Peer der aktuellen Zeile, wie durch ORDER BY definiert . Das Handbuch:

Die standardmäßige Framing-Option ist RANGE UNBOUNDED PRECEDING , was dasselbe ist wie RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Mit ORDER BY , setzt dies den Rahmen auf alle Zeilen vom Beginn der Partition bis zur letzten ORDER BY der aktuellen Zeile Peer.

Und das ist genau der Fall was Sie brauchen.

Verwenden Sie count(*) statt count(id) . Es passt besser zu Ihrer Frage ("Anzahl der Zeilen"). Es ist im Allgemeinen etwas schneller als count(id) . Und obwohl wir davon ausgehen könnten, dass id ist NOT NULL , wurde in der Frage nicht angegeben, also count(id) ist falsch , genau genommen, weil NULL-Werte nicht mit count(id) gezählt werden .

Sie können nicht GROUP BY winzige Scheiben auf der gleichen Abfrageebene. Aggregatfunktionen werden vorher angewendet Fensterfunktionen, die Fensterfunktion count(*) würde auf diese Weise nur 1 Zeile pro Minute sehen.
Sie können jedoch SELECT DISTINCT , weil DISTINCT wird nach angewendet Fensterfunktionen.

ORDER BY 1 ist nur eine Abkürzung für ORDER BY date_trunc('minute', "when") hier.
1 ist eine Positionsreferenz Referenz auf den 1. Ausdruck im SELECT Liste.

Verwenden Sie to_char() wenn Sie das Ergebnis formatieren müssen. Wie:

SELECT DISTINCT
       to_char(date_trunc('minute', "when"), 'DD.MM.YYYY HH24:MI') AS minute
     , count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM   mytable
ORDER  BY date_trunc('minute', "when");

Am schnellsten

SELECT minute, sum(minute_ct) OVER (ORDER BY minute) AS running_ct
FROM  (
   SELECT date_trunc('minute', "when") AS minute
        , count(*) AS minute_ct
   FROM   tbl
   GROUP  BY 1
   ) sub
ORDER  BY 1;

Ähnlich wie oben, aber:

Ich verwende eine Unterabfrage, um Zeilen pro Minute zu aggregieren und zu zählen. Auf diese Weise erhalten wir 1 Zeile pro Minute ohne DISTINCT im äußeren SELECT .

Verwenden Sie sum() als Fenster-Aggregatfunktion, um nun die Zählungen aus der Unterabfrage zu addieren.

Ich habe festgestellt, dass dies mit vielen Zeilen pro Minute wesentlich schneller ist.

Minuten ohne Aktivität einbeziehen

Am kürzesten

@GabiMe fragte in einem Kommentar, wie man eine Zeile für jeden bekommt minute im Zeitraum, einschließlich derjenigen, in denen kein Ereignis aufgetreten ist (keine Zeile in der Basistabelle):

SELECT DISTINCT
       minute, count(c.minute) OVER (ORDER BY minute) AS running_ct
FROM  (
   SELECT generate_series(date_trunc('minute', min("when"))
                        ,                      max("when")
                        , interval '1 min')
   FROM   tbl
   ) m(minute)
LEFT   JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute)
ORDER  BY 1;

Generieren Sie mit generate_series() für jede Minute im Zeitraum zwischen dem ersten und dem letzten Ereignis eine Zeile - hier direkt basierend auf aggregierten Werten aus der Subquery.

LEFT JOIN zu allen Zeitstempeln auf die Minute gekürzt und zählen. NULL Werte (wo keine Zeile vorhanden ist) werden nicht zur laufenden Zählung hinzugefügt.

Am schnellsten

Mit CTE:

WITH cte AS (
   SELECT date_trunc('minute', "when") AS minute, count(*) AS minute_ct
   FROM   tbl
   GROUP  BY 1
   ) 
SELECT m.minute
     , COALESCE(sum(cte.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM  (
   SELECT generate_series(min(minute), max(minute), interval '1 min')
   FROM   cte
   ) m(minute)
LEFT   JOIN cte USING (minute)
ORDER  BY 1;

Auch hier aggregieren und zählen Sie im ersten Schritt Zeilen pro Minute, dadurch entfällt die Notwendigkeit für späteres DISTINCT .

Anders als count() , sum() kann NULL zurückgeben . Standardmäßig 0 mit COALESCE .

Mit vielen Zeilen und einem Index auf "when" Diese Version mit einer Unterabfrage war die schnellste unter einigen Varianten, die ich mit Postgres 9.1 - 9.4 getestet habe:

SELECT m.minute
     , COALESCE(sum(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM  (
   SELECT generate_series(date_trunc('minute', min("when"))
                        ,                      max("when")
                        , interval '1 min')
   FROM   tbl
   ) m(minute)
LEFT   JOIN (
   SELECT date_trunc('minute', "when") AS minute
        , count(*) AS minute_ct
   FROM   tbl
   GROUP  BY 1
   ) c USING (minute)
ORDER  BY 1;