Bygger på denne tabel (bruger ikke SQL-søgeordet "dato" som kolonnenavn.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Forespørgsel:
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;
At trække en date
fra fra en anden date
giver et integer
. Da du leder efter på hinanden følgende dage, ville hver næste række være én større . Hvis vi trækker row_number()
fra fra det ender hele streaken i den samme gruppe (grp
) pr. pid
. Så er det nemt at uddele antal pr. gruppe.
grp
beregnes med to subtraktioner, som skal være hurtigst. Et lige så hurtigt alternativ kunne være:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
En multiplikation, en subtraktion. Strengesammenkædning og støbning er dyrere. Test med EXPLAIN ANALYZE
.
Glem ikke at partitionere efter pid
desuden i begge trin, eller du vil uforvarende blande grupper, der burde adskilles.
Brug af en underforespørgsel, da det typisk er hurtigere end en CTE . Der er intet her, som en almindelig underforespørgsel ikke kunne gøre.
Og siden du nævnte det:dense_rank()
er åbenbart ikke nødvendigt her. Grundlæggende row_number()
gør jobbet.