Voortbouwend op deze tabel (zonder het SQL-trefwoord "date" als kolomnaam.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Vraag:
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;
Een date
aftrekken van een andere date
levert een integer
op . Aangezien u naar opeenvolgende dagen zoekt, zou elke volgende rij één groter zijn . Als we row_number()
. aftrekken van daaruit komt de hele streak in dezelfde groep terecht (grp
) per pid
. Dan is het eenvoudig om aantal per groep uit te delen.
grp
wordt berekend met twee aftrekkingen, wat het snelst zou moeten zijn. Een even snel alternatief zou kunnen zijn:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Eén vermenigvuldiging, één aftrekking. String aaneenschakeling en gieten is duurder. Test met EXPLAIN ANALYZE
.
Vergeet niet te partitioneren op pid
bovendien in beide stappen, anders meng je per ongeluk groepen die gescheiden moeten worden.
Een subquery gebruiken, aangezien dat doorgaans sneller is dan een CTE . Er is hier niets dat een gewone subquery niet zou kunnen doen.
En aangezien je het noemde:dense_rank()
is duidelijk niet hier nodig. Basis row_number()
doet het werk.