Testfall:
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]`')
;
Abfrage - gibt nur Tage zurück, an denen ein Eintrag in tbl
vorhanden ist :
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;
Oder - geben Sie alle Tage zurück im angegebenen Bereich, auch wenn es keine Zeilen für den Tag gibt:
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<>hier fummeln
Ergebnis:
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
Das klang zunächst wie ein Job für Fensterfunktionen, aber ich habe keine Möglichkeit gefunden, den passenden Fensterrahmen zu definieren. Außerdem laut Dokumentation:
Aggregatfensterfunktionen erlauben im Gegensatz zu normalen Aggregatfunktionen DISTINCT
nicht oder ORDER BY
innerhalb der Funktionsargumentliste verwendet werden.
Also habe ich es stattdessen mit korrelierten Unterabfragen gelöst. Ich denke, das ist der klügste Weg.
Übrigens, "zwischen besagtem Datum und vor 3 Tagen" wäre ein Zeitraum von 4 Tage. Ihre Definition ist da widersprüchlich.
Etwas kürzer, aber langsamer für ein paar Tage:
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;
Verwandte:
- Generieren von Zeitreihen zwischen zwei Daten in PostgreSQL
- Rollende Anzahl von Zeilen innerhalb eines Zeitintervalls