用户具有多个会话,重叠和单个会话,找出每个用户重叠和非重叠会话的时间

时间:2018-04-13 17:05:49

标签: sql-server gaps-and-islands

username year  month  day     timein                    time out 
xyz      2012  12     05    2012-12-05 08:10:34.500      2012-12-05 11:23:45:508
xyz      2012  12     05    2012-12-05 09:11:14.352      2012-12-05 11:00:45:378
xyz      2012  12     05    2012-12-05 09:50:34.547      2012-12-05 12:23:45:508
xyz      2012  12     05    2012-12-05 12:06:11.119      2012-12-05 01:23:01:290
xyz      2012  12     05    2012-12-05 02:10:34.547      2012-12-05 04:23:45:508

前4行显示重叠会话,最后一行显示非重叠会话。 我需要一个查询来计算该用户的总时间,并显示所有具有计算时间的会话。

1 个答案:

答案 0 :(得分:0)

示例数据。

CREATE TABLE Session
    ([username] varchar(3), [year] int, [month] int, [day] int, [timein] datetime, [timeout] datetime, CHECK([timeout] >= [timein]) )
;

INSERT INTO Session
    ([username], [year], [month], [day], [timein], [timeout])
VALUES
    ('xyz', 2012, 12, 05, '2012-12-05 08:10:34', '2012-12-05 11:23:45'),
    ('xyz', 2012, 12, 05, '2012-12-05 09:11:14', '2012-12-05 11:00:45'),
    ('xyz', 2012, 12, 05, '2012-12-05 09:50:34', '2012-12-05 12:23:45'),
    ('xyz', 2012, 12, 05, '2012-12-05 12:06:11', '2012-12-05 13:23:01'),
    ('xyz', 2012, 12, 05, '2012-12-05 02:10:34', '2012-12-05 04:23:45')
;

当用户的行按timein排序时,我们知道如果当前行的timein小于或等于此行所见的timeout值没有开始新的会议。否则它会启动一个新会话。

WITH T1
     AS (SELECT *,
                MAX(timeout) OVER (PARTITION BY username ORDER BY timein) AS max_time_out_so_far
         FROM   Session),
     T2
     AS (SELECT *,
                LAG(max_time_out_so_far) OVER (PARTITION BY username ORDER BY timein) AS prev_max_time_out_so_far
         FROM   T1),
     T3
     AS (SELECT *,
                SUM(session_start) OVER (PARTITION BY username ORDER BY timein) AS session_group
         FROM   T2
                CROSS APPLY(SELECT CASE WHEN timein <= prev_max_time_out_so_far THEN 0 ELSE 1 END) CA(session_start))
SELECT *
FROM   T3 
ORDER BY timein

上述样本数据的结果是

+----------+------+-------+-----+-------------------------+-------------------------+-------------------------+--------------------------+---------------+---------------+
| username | year | month | day |         timein          |         timeout         |   max_time_out_so_far   | prev_max_time_out_so_far | session_start | session_group |
+----------+------+-------+-----+-------------------------+-------------------------+-------------------------+--------------------------+---------------+---------------+
| xyz      | 2012 |    12 |   5 | 2012-12-05 02:10:34.000 | 2012-12-05 04:23:45.000 | 2012-12-05 04:23:45.000 | NULL                     |             1 |             1 |
| xyz      | 2012 |    12 |   5 | 2012-12-05 08:10:34.000 | 2012-12-05 11:23:45.000 | 2012-12-05 11:23:45.000 | 2012-12-05 04:23:45.000  |             1 |             2 |
| xyz      | 2012 |    12 |   5 | 2012-12-05 09:11:14.000 | 2012-12-05 11:00:45.000 | 2012-12-05 11:23:45.000 | 2012-12-05 11:23:45.000  |             0 |             2 |
| xyz      | 2012 |    12 |   5 | 2012-12-05 09:50:34.000 | 2012-12-05 12:23:45.000 | 2012-12-05 12:23:45.000 | 2012-12-05 11:23:45.000  |             0 |             2 |
| xyz      | 2012 |    12 |   5 | 2012-12-05 12:06:11.000 | 2012-12-05 13:23:01.000 | 2012-12-05 13:23:01.000 | 2012-12-05 12:23:45.000  |             0 |             2 |
+----------+------+-------+-----+-------------------------+-------------------------+-------------------------+--------------------------+---------------+---------------+

session_group只是到目前为止遇到的session_start个标记的总计。

这可以在分组查询中使用,如下面的完整示例所示。

WITH T1
     AS (SELECT *,
                MAX(timeout) OVER (PARTITION BY username ORDER BY timein) AS max_time_out_so_far
         FROM   Session),
     T2
     AS (SELECT *,
                CASE
                  WHEN timein <= LAG(max_time_out_so_far) OVER (PARTITION BY username ORDER BY timein)
                    THEN 0
                  ELSE 1
                END AS session_start
         FROM   T1),
     T3
     AS (SELECT *,
                SUM(session_start) OVER (PARTITION BY username ORDER BY timein) AS session_group
         FROM   T2),
     T4
     AS (SELECT username,
                session_group,
                MIN(timein)                                 AS timein,
                MAX(timeout)                                AS timeout,
                DATEDIFF(SECOND, MIN(timein), MAX(timeout)) AS session_duration
         FROM   T3
         GROUP  BY username,
                   session_group)
SELECT CASE WHEN GROUPING(username) = 0 THEN username ELSE 'TOTAL' END AS username,
       session_group,
       timein,
       timeout,
       SUM(session_duration) AS session_duration
FROM   T4
GROUP  BY GROUPING SETS ( ( username, session_group, timein, timeout ), ( ) ) 

返回

+----------+---------------+-------------------------+-------------------------+------------------+
| username | session_group |         timein          |         timeout         | session_duration |
+----------+---------------+-------------------------+-------------------------+------------------+
| xyz      | 1             | 2012-12-05 02:10:34.000 | 2012-12-05 04:23:45.000 |             7991 |
| xyz      | 2             | 2012-12-05 08:10:34.000 | 2012-12-05 13:23:01.000 |            18747 |
| TOTAL    | NULL          | NULL                    | NULL                    |            26738 |
+----------+---------------+-------------------------+-------------------------+------------------+