Извадете 30 минути от края (или началото) на всеки времеви диапазон. След това основно продължете, както е описано в моя рефериран „прост“ отговор (настройване за 30 минути в правилната посока навсякъде). Диапазони, по-кратки от 30 минути, се елиминират a priori - което има смисъл, тъй като те никога не могат да бъдат част от 30-минутен период на непрекъснато припокриване. Освен това прави заявката по-бърза.
Изчисляване за всички дни през октомври 2019 г. (примерен диапазон):
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 тук
Имайте предвид, че LOCALTIMESTAMP
зависи от часовата зона на текущата сесия. Помислете дали да не използвате timestamptz във вашата таблица и CURRENT_TIMESTAMP
вместо. Вижте: