Sal*_*hid -1 sql-server query aggregate
我正在寻找提供月份和年份的查询,并显示所选月份的 4(或 5)周,并且每周应显示本周同时登录用户的最大数量。最大用户数应根据 SQL Server 表 usersession 计算。同时意味着同时登录,即它们具有重叠的登录日期时间/注销日期时间。。
因此,如果存在并发用户,则应给出不同用户的数量,如果该日期存在用户但不并发,则应返回 1,否则返回 0。
我知道这很难。
Itzik Ben-Gan 等人提供了标准解决方案来解决此问题。
\n关于“最大并发登录不同用户数”的确切含义存在疑问。如果这意味着每个用户可能有多个并发会话,而您只想对每个用户计算一个会话,那么我们需要第一步将会话数据压平为压缩间隔。
\n下图转载自Itzik Ben-Gan 的Packing Intervals :
\n\n\n\n
如果需要此步骤,您可以找到该文章中介绍的三个有效的解决方案。下面给出了应用于样本数据的基于窗口函数的函数:
\nWITH \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;\nRun Code Online (Sandbox Code Playgroud)\n现在,示例数据不包含同一用户的重叠会话实例,因此上述打包是无操作的。不过,一般来说,它会压缩间隔,如图所示。
\nItzik 在计算并发会话数第 3 部分中报告了以下标准解决方案,该解决方案由 Ben Flanaghan、Arnold Fribble 和 R. Barry Young 提供。此阶段的输入将是 Packing Intervals 阶段的输出(如果需要)。
\n使用示例数据的解决方案如下:
\nWITH\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;\nRun Code Online (Sandbox Code Playgroud)\n请注意,第一个公用表表达式 (UserSessions) 定义了符合正在处理的时间段的数据。同样,对于如何计算开始日期和结束日期存在一些问题,但上述内容应该为您的自定义提供坚实的基础。
\n演示: db<>fiddle
\n间隔打包和并发会话代码可以轻松地包装在一个内联函数中,然后在所需的月份期间使用 每周调用一次APPLY。例如,并发会话函数如下所示:
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;\nRun 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;\nRun 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