Com base nesta tabela (sem usar a palavra-chave SQL "date" como nome da coluna.):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
Consulta:
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;
Subtraindo uma
date
de outra date
produz um integer
. Como você está procurando dias consecutivos, cada linha seguinte seria maior em um . Se subtrairmos row_number()
a partir disso, toda a sequência acaba no mesmo grupo (grp
) por pid
. Então é simples distribuir o número por grupo. grp
é calculado com duas subtrações, que devem ser mais rápidas. Uma alternativa igualmente rápida poderia ser:the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
Uma multiplicação, uma subtração. A concatenação e a conversão de strings são mais caras. Teste com
EXPLAIN ANALYZE
. Não se esqueça de particionar por
pid
adicionalmente em ambos passos, ou você vai misturar inadvertidamente grupos que deveriam ser separados. Usando uma subconsulta, pois geralmente é mais rápida que uma CTE . Não há nada aqui que uma subconsulta simples não possa fazer.
E já que você mencionou:
dense_rank()
obviamente não necessário aqui. Básico row_number()
faz o trabalho.