Przypadek testowy:
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]`')
;
Zapytanie - zwraca tylko dni, w których wpis istnieje w 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;
Lub - zwróć wszystkie dni w określonym zakresie, nawet jeśli nie ma wierszy dla danego dnia:
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<>graj tutaj
Wynik:
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
To brzmiało jak praca dla funkcji okien na początku, ale nie znalazłem sposobu na zdefiniowanie odpowiedniej ramy okna. Ponadto, zgodnie z dokumentacją:
Funkcje okna agregującego, w przeciwieństwie do normalnych funkcji agregujących, nie pozwalają na DISTINCT
lub ORDER BY
do użycia na liście argumentów funkcji.
Więc rozwiązałem to za pomocą skorelowanych podzapytań. Myślę, że to najmądrzejszy sposób.
BTW, „między wspomnianą datą a 3 dniami temu” byłby okresem 4 dni. Twoja definicja jest tam sprzeczna.
Nieco krócej, ale wolniej przez kilka dni:
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;
Powiązane:
- Generowanie szeregów czasowych między dwiema datami w PostgreSQL
- Rocząca liczba wierszy w przedziale czasowym