查找不同用户并发登录的最大数量

Sal*_*hid -1 sql-server query aggregate

我正在寻找提供月份和年份的查询,并显示所选月份的 4(或 5)周,并且每周应显示本周同时登录用户的最大数量。最大用户数应根据 SQL Server 表 usersession 计算。同时意味着同时登录,即它们具有重叠的登录日期时间/注销日期时间。。表和示例

因此,如果存在并发用户,则应给出不同用户的数量,如果该日期存在用户但不并发,则应返回 1,否则返回 0。

我知道这很难。

Pau*_*ite 7

Itzik Ben-Gan 等人提供了标准解决方案来解决此问题。

\n

关于“最大并发登录不同用户数”的确切含义存在疑问。如果这意味着每个用户可能有多个并发会话,而您只想对每个用户计算一个会话,那么我们需要第一步将会话数据压平为压缩间隔

\n

装箱间隔

\n

下图转载自Itzik Ben-Gan 的Packing Intervals :

\n
\n

(c) 伊齐克·本-甘

\n
\n

如果需要此步骤,您可以找到该文章中介绍的三个有效的解决方案。下面给出了应用于样本数据的基于窗口函数的函数:

\n
WITH \n    C1 AS\n    (\n        SELECT\n            US.UserName,\n            ts = US.LoginDateTime,\n            event_type = +1,\n            sub = 1\n        FROM dbo.UserSession AS US\n        UNION ALL\n        SELECT\n            US.UserName,\n            ts = US.LogoutDateTime,\n            event_type = -1,\n            sub = 0\n        FROM dbo.UserSession AS US\n    ),\n    C2 AS\n    (\n        SELECT \n            C1.*,\n            cnt =\n                SUM(C1.event_type) OVER(\n                    PARTITION BY C1.UserName \n                    ORDER BY ts, C1.event_type DESC\n                    ROWS UNBOUNDED PRECEDING) - sub\n        FROM C1\n    ),\n    C3 AS\n    (\n        SELECT\n            C2.UserName,\n            C2.ts,\n            grpnum =\n                FLOOR\n                (\n                    (\n                        ROW_NUMBER() OVER (\n                            PARTITION BY C2.UserName \n                            ORDER BY C2.ts) - 1\n                    ) / 2 + 1\n                )\n        FROM C2\n        WHERE\n            C2.cnt = 0\n    )\nSELECT\n    C3.UserName,\n    LoginDateTime = MIN(ts),\n    LogoutDateTime = MAX(ts)\nFROM C3\nGROUP BY\n    C3.UserName,\n    C3.grpnum\nORDER BY\n    LoginDateTime;\n
Run Code Online (Sandbox Code Playgroud)\n

现在,示例数据不包含同一用户的重叠会话实例,因此上述打包是无操作的。不过,一般来说,它会压缩间隔,如图所示。

\n

并发会话数

\n

Itzik 在计算并发会话数第 3 部分中报告了以下标准解决方案,该解决方案由 Ben Flanaghan、Arnold Fribble 和 R. Barry Young 提供。此阶段的输入将是 Packing Intervals 阶段的输出(如果需要)。

\n

使用示例数据的解决方案如下:

\n
WITH\n    UserSessions AS\n    (\n        SELECT * \n        FROM dbo.UserSession AS US\n        WHERE\n            US.LoginDateTime >= CONVERT(datetime, \'20180429\', 112)\n            AND US.LogoutDateTime < CONVERT(datetime, \'20180501\', 112)\n    ),\n    C1 AS\n    (\n        SELECT\n            ts = US.LoginDateTime,\n            event_type = +1,\n            start_ordinal = ROW_NUMBER() OVER (\n                ORDER BY US.LoginDateTime)\n        FROM UserSessions AS US\n        UNION ALL\n        SELECT\n            ts = US.LogoutDateTime,\n            event_type = -1,\n            start_ordinal = CONVERT(bigint, NULL)\n        FROM UserSessions AS US\n    ),\n    C2 AS\n    (\n        SELECT \n            *,\n            start_or_end_ordinal =\n                ROW_NUMBER() OVER (\n                    ORDER BY C1.ts, C1.event_type)\n      FROM C1\n    )\nSELECT\n    mx = MAX(2 * start_ordinal - start_or_end_ordinal)\nFROM C2\nWHERE \n    C2.event_type = 1;\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,第一个公用表表达式 (UserSessions) 定义了符合正在处理的时间段的数据。同样,对于如何计算开始日期和结束日期存在一些问题,但上述内容应该为您的自定义提供坚实的基础。

\n

演示: db<>fiddle

\n

完整的解决方案

\n

间隔打包和并发会话代码可以轻松地包装在一个内联函数中,然后在所需的月份期间使用 每周调用一次APPLY。例如,并发会话函数如下所示:

\n
CREATE FUNCTION dbo.MaxConcurrentUserSessions\n(\n    @WeekStart datetime\n)\nRETURNS table\nWITH SCHEMABINDING AS\nRETURN\n    WITH\n        WeekSessions AS\n        (\n            SELECT\n                US.UserName,\n                US.LoginDateTime,\n                US.LogoutDateTime \n            FROM dbo.UserSession AS US\n            WHERE\n                US.LoginDateTime >= @WeekStart\n                AND US.LogoutDateTime < DATEADD(DAY, 7, @WeekStart)\n        ),\n        C1 AS\n        (\n            SELECT\n                ts = WS.LoginDateTime,\n                event_type = +1,\n                start_ordinal = ROW_NUMBER() OVER (\n                    ORDER BY WS.LoginDateTime)\n            FROM WeekSessions AS WS\n            UNION ALL\n            SELECT\n                ts = WS.LogoutDateTime,\n                event_type = -1,\n                start_ordinal = CONVERT(bigint, NULL)\n            FROM WeekSessions AS WS\n        ),\n        C2 AS\n        (\n            SELECT \n                C1.ts,\n                C1.event_type,\n                C1.start_ordinal,\n                start_or_end_ordinal =\n                    ROW_NUMBER() OVER (\n                        ORDER BY C1.ts, C1.event_type)\n          FROM C1\n        )\n    SELECT\n        mx = MAX(2 * start_ordinal - start_or_end_ordinal)\n    FROM C2\n    WHERE \n        C2.event_type = 1;\n
Run Code Online (Sandbox Code Playgroud)\n

为简洁起见,省略打包阶段(并且因为实际上可能不需要),结果(例如 2018 年 4 月)将通过以下方式获得:

\n
-- The month to report on\nDECLARE @MonthStart datetime = CONVERT(datetime, \'20180401\');\n\n-- Holds the start of each week in the given month\nDECLARE @Weeks AS table (WeekStart datetime PRIMARY KEY);\n\n-- Find the week start dates\nINSERT @Weeks (WeekStart)\nSELECT CA.WeekStart\nFROM master.dbo.spt_values AS SV\nCROSS APPLY\n(\n    VALUES (DATEADD(DAY, 7 * SV.number, @MonthStart))\n) AS CA (WeekStart)\nWHERE SV.[type] = \'P\'\nAND SV.number BETWEEN 0 AND 5\nAND CA.WeekStart < DATEADD(MONTH, 1, @MonthStart);\n\n-- Find the concurrent sessions for all weeks\nSELECT \n    W.WeekStart,\n    mx = ISNULL(MCUS.mx, 0)\nFROM @Weeks AS W\nCROSS APPLY dbo.MaxConcurrentUserSessions(W.WeekStart) AS MCUS\nORDER BY W.WeekStart;\n
Run Code Online (Sandbox Code Playgroud)\n

这给出:

\n
\n\xe2\x95\x94\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90 \xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2 \x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95 \x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x97\n\xe2\x95 \x91 WeekStart \xe2\x95\x91 mx \xe2\x95\x91\n\xe2\x95\xa0\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2 \x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95 \x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90 \xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2 \x95\x90\xe2\x95\xa3\n\xe2\x95\x91 2018-04-01 00:00:00.000 \xe2\x95\x91 1 \xe2\x95\x91\n\xe2\x95\x91 2018 -04-08 00:00:00.000 \xe2\x95\x91 0 \xe2\x95\x91\n\xe2\x95\x91 2018-04-15 00:00:00.000 \xe2\x95\x91 0 \xe2\ x95\x91\n\xe2\x95\x91 2018-04-22 00:00:00.000 \xe2\x95\x91 0 \xe2\x95\x91\n\xe2\x95\x91 2018-04-29 00:00 :00.000 \xe2\x95\x91 3 \xe2\x95\x91\n\xe2\x95\x9a\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\ x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\ x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\ xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\ x95\x90\xe2\x95\x9d\n
\n

演示: db<>fiddle

\n