Kurangi 30 menit dari akhir (atau awal) setiap rentang waktu. Kemudian pada dasarnya lanjutkan seperti yang diuraikan dalam referensi jawaban "sederhana" saya (menyesuaikan selama 30 menit ke arah yang benar di mana-mana). Rentang yang lebih pendek dari 30 menit dihilangkan secara apriori - yang masuk akal karena tidak pernah dapat menjadi bagian dari periode tumpang tindih terus menerus selama 30 menit. Juga membuat kueri lebih cepat.
Menghitung untuk semua hari di Oktober 2019 (rentang contoh):
WITH range AS (SELECT timestamp '2019-10-01' AS start_ts -- incl. lower bound
, timestamp '2019-11-01' AS end_ts) -- excl. upper bound
, cte AS (
SELECT userid, starttime
-- default to current timestamp if NULL
, COALESCE(endtime, localtimestamp) - interval '30 min' AS endtime
FROM usersessions, range r
WHERE starttime < r.end_ts -- count overlaps *starting* in outer time range
AND (endtime >= r.start_ts + interval '30 min' OR endtime IS NULL)
)
, ct AS (
SELECT ts, sum(ct) OVER (ORDER BY ts, ct) AS session_ct
FROM (
SELECT endtime AS ts, -1 AS ct FROM cte
UNION ALL
SELECT starttime , +1 FROM cte
) sub
)
SELECT ts::date, max(session_ct) AS max_concurrent_sessions
FROM ct, range r
WHERE ts >= r.start_ts
AND ts < r.end_ts -- crop outer time range
GROUP BY ts::date
ORDER BY 1;
db<>fiddle di sini
Ketahuilah bahwa LOCALTIMESTAMP
tergantung pada zona waktu sesi saat ini. Pertimbangkan untuk menggunakan timestamptz di tabel Anda dan CURRENT_TIMESTAMP
alih-alih. Lihat: