Opierając się na tej tabeli (bez użycia słowa kluczowego SQL „date” jako nazwę kolumny.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Zapytanie:
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;
Odejmowanie date
z innej date
zwraca integer
. Ponieważ szukasz kolejnych dni, każdy kolejny wiersz byłby większy o jeden . Jeśli odejmiemy row_number()
od tego cała passa kończy się w tej samej grupie (grp
) na pid
. Wtedy łatwo jest rozdzielić liczbę na grupę.
grp
jest obliczany za pomocą dwóch odejmowań, które powinny być najszybsze. Równie szybką alternatywą może być:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Jedno mnożenie, jedno odejmowanie. Łączenie i rzutowanie ciągów jest droższe. Przetestuj za pomocą EXPLAIN ANALYZE
.
Nie zapomnij podzielić według pid
dodatkowo w obu kroki lub przypadkowo wymieszasz grupy, które powinny być rozdzielone.
Korzystanie z podzapytania, ponieważ jest to zwykle szybsze niż CTE . Nie ma tu niczego, czego nie mogłoby zrobić zwykłe podzapytanie.
A skoro o tym wspomniałeś:dense_rank()
jest oczywiście nie konieczne tutaj. Podstawowe row_number()
wykonuje swoją pracę.