Ziehen Sie 30 Minuten vom Ende (oder Beginn) jedes Zeitbereichs ab. Gehen Sie dann grundsätzlich so vor, wie in meiner referenzierten "einfachen" Antwort beschrieben (Anpassung für die 30 min überall in die richtige Richtung). Bereiche, die kürzer als 30 Minuten sind, werden a priori eliminiert – was sinnvoll ist, da diese niemals Teil einer 30-minütigen Periode kontinuierlicher Überschneidung sein können. Macht die Abfrage auch schneller.
Berechnung für alle Tage im Oktober 2019 (Beispielbereich):
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 hier
Beachten Sie, dass LOCALTIMESTAMP
hängt von der Zeitzone der aktuellen Sitzung ab. Erwägen Sie die Verwendung von timestamptz in Ihrer Tabelle und CURRENT_TIMESTAMP
stattdessen. Siehe: