Caso de prueba:
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:devuelve solo los días en los que existe una entrada en 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;
O - volver todos los días en el rango especificado, incluso si no hay filas para el día:
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<>violín aquí
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
Esto sonó como un trabajo para las funciones de ventana al principio, pero no encontré una manera de definir el marco de ventana adecuado. Además, según la documentación:
Las funciones de ventana agregadas, a diferencia de las funciones agregadas normales, no permiten DISTINCT
o ORDER BY
para ser utilizado dentro de la lista de argumentos de la función.
Así que lo resolví con subconsultas correlacionadas en su lugar. Supongo que esa es la forma más inteligente.
Por cierto, "entre dicha fecha y hace 3 días" sería un período de 4 dias. Su definición es contradictoria allí.
Ligeramente más corto, pero más lento durante algunos días:
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:
- Generando series de tiempo entre dos fechas en PostgreSQL
- Recuento continuo de filas dentro del intervalo de tiempo