Caso di prova:
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]`')
;
Query:restituisce solo i giorni in cui esiste una voce in 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;
Oppure - restituisci tutti i giorni nell'intervallo specificato, anche se non ci sono righe per il giorno:
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<>gioca qui
Risultato:
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
Questo suonava come un lavoro per le funzioni della finestra all'inizio, ma non ho trovato un modo per definire la cornice della finestra adatta. Inoltre, per documentazione:
Le funzioni di aggregazione della finestra, a differenza delle normali funzioni di aggregazione, non consentono DISTINCT
o ORDER BY
da utilizzare nell'elenco degli argomenti della funzione.
Quindi l'ho risolto con subquery correlate. Immagino sia il modo più intelligente.
A proposito, "tra detta data e 3 giorni fa" sarebbe un periodo di 4 giorni. La tua definizione è contraddittoria lì.
Leggermente più breve, ma più lento per alcuni giorni:
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;
Correlati:
- Generazione di serie temporali tra due date in PostgreSQL
- Numero progressivo di righe entro l'intervallo di tempo