计算并发用户会话的最大数量

时间:2019-12-17 14:28:24

标签: sql postgresql overlap gaps-and-islands postgresql-9.6

我在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分钟

1 个答案:

答案 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中使用。参见: