Kasus uji:
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]`')
;
Kueri - hanya mengembalikan hari di mana ada entri di 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;
Atau - kembalikan sepanjang hari dalam rentang yang ditentukan, meskipun tidak ada baris untuk hari itu:
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<>main biola di sini
Hasil:
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
Ini terdengar seperti pekerjaan untuk fungsi jendela pada awalnya, tetapi saya tidak menemukan cara untuk menentukan bingkai jendela yang sesuai. Juga, per dokumentasi:
Fungsi jendela agregat, tidak seperti fungsi agregat normal, tidak mengizinkan
DISTINCT
atauORDER BY
untuk digunakan dalam daftar argumen fungsi.
Jadi saya menyelesaikannya dengan subqueries yang berkorelasi sebagai gantinya. Saya rasa itu cara paling cerdas.
BTW, "antara tanggal tersebut dan 3 hari yang lalu" akan menjadi periode 4 hari. Definisi Anda kontradiktif di sana.
Sedikit lebih pendek, tetapi lebih lambat selama beberapa hari:
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;
Terkait:
- Membuat deret waktu antara dua tanggal di PostgreSQL
- Jumlah baris yang bergulir dalam interval waktu