Bygger vidare på denna tabell (använder inte SQL-sökordet "datum" som kolumnnamn.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Fråga:
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;
Subtrahera ett date
från ett annat date
ger ett integer
. Eftersom du letar efter dagar i följd blir varje nästa rad större med en . Om vi subtraherar row_number()
från det hamnar hela raden i samma grupp (grp
) per pid
. Då är det enkelt att dela ut antal per grupp.
grp
beräknas med två subtraktioner, som bör vara snabbast. Ett lika snabbt alternativ kan vara:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
En multiplikation, en subtraktion. Strängsammansättning och gjutning är dyrare. Testa med EXPLAIN ANALYZE
.
Glöm inte att partitionera med pid
dessutom i båda steg, eller så blandar du oavsiktligt grupper som borde separeras.
Använder en underfråga, eftersom det vanligtvis är snabbare än en CTE . Det finns inget här som en vanlig underfråga inte kunde göra.
Och eftersom du nämnde det:dense_rank()
är uppenbarligen inte nödvändigt här. Grundläggande row_number()
gör jobbet.