Caso de teste:
CREATE TABLE tbl (date date, email text);
INSERT INTO tbl VALUES
('2012-01-01', '[email protected]')
, ('2012-01-01', '[email protected]')
, ('2012-01-01', '[email protected]')
, ('2012-01-02', '[email protected]')
, ('2012-01-02', '[email protected]')
, ('2012-01-03', '[email protected]')
, ('2012-01-04', '[email protected]')
, ('2012-01-05', '[email protected]')
, ('2012-01-05', '[email protected]')
, ('2012-01-06', '[email protected]')
, ('2012-01-06', '[email protected]')
, ('2012-01-06', '[email protected]`')
;
Consulta - retorna apenas os dias em que existe uma entrada em
tbl
:SELECT date
,(SELECT count(DISTINCT email)
FROM tbl
WHERE date BETWEEN t.date - 2 AND t.date -- period of 3 days
) AS dist_emails
FROM tbl t
WHERE date BETWEEN '2012-01-01' AND '2012-01-06'
GROUP BY 1
ORDER BY 1;
Ou - retornar todos os dias no intervalo especificado, mesmo se não houver linhas para o dia:
SELECT date
,(SELECT count(DISTINCT email)
FROM tbl
WHERE date BETWEEN g.date - 2 AND g.date
) AS dist_emails
FROM (SELECT generate_series(timestamp '2012-01-01'
, timestamp '2012-01-06'
, interval '1 day')::date) AS g(date);
db<>mexa aqui
Resultado:
day | dist_emails
-----------+------------
2012-01-01 | 3
2012-01-02 | 3
2012-01-03 | 3
2012-01-04 | 3
2012-01-05 | 1
2012-01-06 | 2
Isso soou como um trabalho para funções de janela no início, mas não encontrei uma maneira de definir o quadro de janela adequado. Além disso, por documentação:
Funções de janela agregadas, ao contrário das funções agregadas normais, não permitemDISTINCT
ouORDER BY
para ser usado dentro da lista de argumentos da função.
Então resolvi isso com subconsultas correlacionadas. Acho que essa é a maneira mais inteligente.
BTW, "entre a referida data e 3 dias atrás" seria um período de 4 dias. Sua definição é contraditória aí.
Um pouco mais curto, mas mais lento por alguns dias:
SELECT g.date, count(DISTINCT email) AS dist_emails
FROM (SELECT generate_series(timestamp '2012-01-01'
, timestamp '2012-01-06'
, interval '1 day')::date) AS g(date)
LEFT JOIN tbl t ON t.date BETWEEN g.date - 2 AND g.date
GROUP BY 1
ORDER BY 1;
Relacionado:
- Gerando séries temporais entre duas datas no PostgreSQL
- Contagem contínua de linhas dentro do intervalo de tempo