Déduire 30 minutes de la fin (ou du début) de chaque plage horaire. Ensuite, procédez comme indiqué dans ma réponse "simple" référencée (en réglant les 30 min dans le bon sens partout). Les plages inférieures à 30 minutes sont éliminées a priori - ce qui est logique car celles-ci ne peuvent jamais faire partie d'une période de chevauchement continu de 30 minutes. Rend également la requête plus rapide.
Calcul pour tous les jours d'octobre 2019 (exemple de plage) :
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<>violon ici
Sachez que LOCALTIMESTAMP
dépend du fuseau horaire de la session en cours. Envisagez d'utiliser timestamptz dans votre tableau et CURRENT_TIMESTAMP
Au lieu. Voir :