Untungnya, Anda menggunakan PostgreSQL. Fungsi jendela generate_series()
adalah temanmu.
Kasus uji
Diberikan tabel pengujian berikut (yang Anda seharusnya disediakan):
CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
, timestamp '2018-05-08'
, interval '7 min') + random() * interval '7 min';
Satu acara untuk setiap 7 menit (ditambah 0 hingga 7 menit, secara acak).
Solusi dasar
Kueri ini menghitung peristiwa untuk interval waktu arbitrer apa pun. 17 menit dalam contoh:
WITH grid AS (
SELECT start_time
, lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
FROM (
SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
FROM event
) sub
)
SELECT start_time, count(e.ts) AS events
FROM grid g
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.end_time
GROUP BY start_time
ORDER BY start_time;
-
Kueri mengambil
ts
minimum dan maksimum dari tabel dasar untuk mencakup rentang waktu yang lengkap. Anda dapat menggunakan rentang waktu arbitrer sebagai gantinya. -
Berikan apa saja interval waktu sesuai kebutuhan.
-
Menghasilkan satu baris untuk setiap slot waktu. Jika tidak ada kejadian yang terjadi selama interval tersebut, hitungannya adalah
0
. -
Pastikan untuk menangani batas atas dan bawah dengan benar:
- Hasil tak terduga dari kueri SQL dengan stempel waktu ANTARA
-
Fungsi jendela
lead()
memiliki fitur yang sering diabaikan:ini dapat memberikan default ketika tidak ada baris terdepan. Menyediakan'infinity'
dalam contoh. Jika tidak, interval terakhir akan dipotong dengan batas atasNULL
.
Setara minimal
Kueri di atas menggunakan CTE dan lead()
dan sintaksis verbose. Elegan dan mungkin lebih mudah dipahami, tetapi sedikit lebih mahal. Ini adalah versi yang lebih pendek, lebih cepat, dan minimal:
SELECT start_time, count(e.ts) AS events
FROM (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '17 min'
GROUP BY 1
ORDER BY 1;
Contoh untuk "setiap 15 menit dalam seminggu terakhir"`
Dan memformat dengan to_char()
.
SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM generate_series(date_trunc('day', localtimestamp - interval '7 days')
, localtimestamp
, interval '15 min') g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '15 min'
GROUP BY start_time
ORDER BY start_time;
Masih ORDER BY
dan GROUP BY
pada stempel waktu yang mendasari nilai , bukan pada string yang diformat. Itu lebih cepat dan lebih dapat diandalkan.
db<>main biola di sini
Jawaban terkait menghasilkan jumlah berjalan selama jangka waktu:
- PostgreSQL:menjalankan hitungan baris untuk kueri 'menurut menit'