Надграждане върху тази таблица (без използване на SQL ключовата дума „дата“ като име на колона.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Заявка:
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;
Изваждане на date
от друга date
дава integer
. Тъй като търсите последователни дни, всеки следващ ред ще бъде по-голям с един . Ако извадим row_number()
от това цялата поредица завършва в една и съща група (grp
) за pid
. Тогава е лесно да раздадете брой на група.
grp
се изчислява с две изваждания, които трябва да са най-бързи. Също толкова бърза алтернатива може да бъде:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Едно умножение, едно изваждане. Конкатенацията и прехвърлянето на низове е по-скъпо. Тествайте с EXPLAIN ANALYZE
.
Не забравяйте да разделите по pid
допълнително в и двете стъпки, или по невнимание ще смесите групи, които трябва да бъдат разделени.
Използване на подзаявка, тъй като това обикновено е по-бързо от CTE . Тук няма нищо, което обикновена подзаявка да не може да направи.
И тъй като го споменахте:dense_rank()
очевидно не необходимо тук. Основен row_number()
върши работата.