Basandosi su questa tabella (non utilizzando la parola chiave SQL "date" come nome della colonna.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Domanda:
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;
Sottraendo una date
da un'altra date
restituisce un integer
. Dato che stai cercando giorni consecutivi, ogni riga successiva sarebbe maggiore di uno . Se sottraiamo row_number()
da ciò, l'intera serie finisce nello stesso gruppo (grp
) per pid
. Quindi è semplice distribuire il numero per gruppo.
grp
è calcolato con due sottrazioni, che dovrebbero essere più veloci. Un'alternativa altrettanto veloce potrebbe essere:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Una moltiplicazione, una sottrazione. La concatenazione e il casting di stringhe sono più costosi. Prova con EXPLAIN ANALYZE
.
Non dimenticare di partizionare per pid
inoltre in entrambi passaggi o mescolerai inavvertitamente i gruppi che dovrebbero essere separati.
Utilizzo di una sottoquery, poiché in genere è più veloce di un CTE . Non c'è niente qui che una semplice sottoquery non possa fare.
E poiché l'hai menzionato:dense_rank()
ovviamente non necessario qui. row_number()
di base
fa il suo lavoro.