Aufbauend auf dieser Tabelle (ohne das SQL-Schlüsselwort "date" als Spaltenname.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Abfrage:
SELECT pid, the_date
, row_number() OVER (PARTITION BY pid, grp ORDER BY the_date) AS in_streak
FROM (
SELECT *
, the_date - '2000-01-01'::date
- row_number() OVER (PARTITION BY pid ORDER BY the_date) AS grp
FROM tbl
) sub
ORDER BY pid, the_date;
Subtrahieren eines date
von einem anderen date
ergibt eine integer
. Da Sie nach aufeinanderfolgenden Tagen suchen, wäre jede nächste Zeile um eins größer . Wenn wir row_number()
subtrahieren Dadurch landet der gesamte Streak in derselben Gruppe (grp
) per pid
. Dann ist es einfach, die Anzahl pro Gruppe zu verteilen.
grp
wird mit zwei Subtraktionen berechnet, was am schnellsten sein sollte. Eine ebenso schnelle Alternative könnte sein:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Eine Multiplikation, eine Subtraktion. Das Verketten und Casting von Zeichenfolgen ist teurer. Testen Sie mit EXPLAIN ANALYZE
.
Vergessen Sie nicht, nach pid
zu partitionieren zusätzlich in beiden oder Sie vermischen versehentlich Gruppen, die getrennt werden sollten.
Verwenden einer Unterabfrage, da diese normalerweise schneller ist als ein CTE . Hier gibt es nichts, was eine einfache Unterabfrage nicht tun könnte.
Und da du es schon erwähnt hast:dense_rank()
ist offensichtlich nicht hier notwendig. Grundlegende row_number()
macht den Job.