我在PostgreSQL 9.6中有一个UserSession
表,用于存储用户的登录和注销时间,我想计算并发会话的最大数量-仅当它们重叠至少为时才被认为是并发的30分钟。
示例
userid | starttime | endtime
------------+---------------------------+--------------------------
1 | 01-Oct-19 6:00:00 AM | 01-Oct-19 11:10:00 AM
2 | 01-Oct-19 11:00:00 AM | 01-Oct-19 4:00:00 PM
3 | 01-Oct-19 10:30:00 AM | 01-Oct-19 4:00:00 PM
在这里,会话1和2不是并发的,因为它们仅重叠10分钟,而会话1和3是并发的,因为它们重叠30分钟以上,所以结果是2个并发会话 。
注意::仅当所有n个会话都重叠至少30分钟时,结果才是n。
表定义
CREATE TABLE UserSessions (
SessionID bigserial NOT NULL,
UserID bigint NOT NULL,
StartTime timestamp NOT NULL,
EndTime timestamp NULL,
OrganisationID bigint NOT NULL,
CONSTRAINT PK_SessionsID PRIMARY KEY(SessionID),
CONSTRAINT FK_UserID FOREIGN KEY(UserID) REFERENCES GlobalUsers(UserID),
CONSTRAINT FK_OrganisationID FOREIGN KEY(OrganisationID) REFERENCES Organisations(OrganisationID)
);
类似问题
这里有一个类似的问题:Count max number of concurrent user session,但是在同一时间点存在并发方式,对于我来说,我需要检查它们是否至少重叠30分钟
答案 0 :(得分:2)
从每个时间范围的结束(或开始)减去30分钟。然后基本上按照我的referenced "simple" answer中所述进行操作(在正确的方向进行30分钟的调整)。小于30分钟的范围会被事先消除-这是有道理的,因为它们永远不会成为30分钟连续重叠的一部分。也使查询更快。
计算2019年10月的所有天数(示例范围):
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 <>提琴here
请注意,LOCALTIMESTAMP
取决于当前会话的时区。考虑在表中使用timestamptz,而在CURRENT_TIMESTAMP
中使用。参见: