计算某些记录不完整时登录和注销时间之间的持续时间

时间:2016-05-24 12:34:44

标签: sql sql-server tsql intervals date-difference

我想计算用户在网站上花费的总时间。有3种情况。

  1. 存在用户登录时间和注销时间的记录。

    - >总时间应为登录和退出之间的时差总和。

  2. 有用户登录时间的记录,但没有退出时间。

    - >总时间应标记为-1。

  3. 用户多次登录,只有一次退出时间。

    - >总时间应该是最早登录时间和退出时间之间的时差总和。

  4. 我的表

    CREATE TABLE #my_table
    (
        id BIGINT                      IDENTITY PRIMARY KEY
        ,userID                        INT
        ,login_time                    DATETIME
        ,logout_time                   DATETIME
    );
    
    INSERT INTO #my_table
              SELECT 222222, '2016-05-19 01:06:00.000', '2016-05-19 01:10:00.000'
    UNION ALL SELECT 222222, '2016-05-19 01:12:00.000', '2016-05-19 01:20:00.000'
    UNION ALL SELECT 333333, '2016-05-24 14:44:00.000', '2016-05-24 14:47:00.000'
    UNION ALL SELECT 333333, '2016-05-24 14:59:00.000', NULL
    UNION ALL SELECT 444444, '2016-05-24 14:48:00.000', '2016-05-24 14:49:00.000'
    UNION ALL SELECT 444444, '2016-05-24 14:50:00.000', NULL
    UNION ALL SELECT 444444, '2016-05-24 14:51:00.000', NULL
    UNION ALL SELECT 444444, '2016-05-24 14:53:00.000', '2016-05-24 14:59:00.000'
    

    预期结果

    enter image description here

    对于大多数情况,数据库中捕获的记录将是案例1,但有时也会捕获案例2和案例3。我需要一个脚本来计算所有案例的总登录时间。

    我该如何查询?

2 个答案:

答案 0 :(得分:1)

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
   Cancel = MsgBox("Please confirm cancellation", vbOKCancel + vbQuestion) = vbCancel
End Sub

答案 1 :(得分:1)

下面的查询多次使用ROW_NUMBER函数来选择所需的行,LEAD函数在logout_time为NULL时“向前看”。自SQL Server 2012以来,LEAD可用。

逐步运行查询,CTE-by-CTE并检查中间结果以了解其工作原理。

CTE_Groups是一个经典gaps-and-islands查询,用于在logout_time中标记连续NULL的行。

CTE_RN以这种方式为行分配数字,logout_time中的连续NULL获得连续数字。此结果在CTE_Fixed中过滤,以便每组NULL只获得第一行。如果logout_time为NULL,则LEAD函数用于从下一行中选择一个值以生成fixed_logout_time

具有NULL logout_time的行和具有非NULL logout_time的下一行将在CTE_Fixed中一起列出。我们需要从这些对中选择一行。相同方法 - 在ROW_NUMBER中使用CTE_FixedRN并选择CTE_Sum中的第一行。

然后我们可以在几分钟内计算Duration,并将总和按userID分组。

如果没有非NULL logout_timeDATEDIFF将返回NULL,将替换为一些大的负数。在最终SELECT中,否定Duration将替换为-1,表示上一个时间间隔仍处于打开状态。

WITH
CTE_Groups
AS
(
    SELECT
        userID
        ,login_time
        ,logout_time
        ,ROW_NUMBER() 
            OVER(PARTITION BY userID ORDER BY login_time)
        - ROW_NUMBER() 
            OVER(PARTITION BY userID, logout_time ORDER BY login_time) AS GroupNumber
    FROM #my_table
)
,CTE_RN
AS
(
    SELECT
        userID
        ,login_time
        ,logout_time
        ,ROW_NUMBER()
            OVER(PARTITION BY userID, GroupNumber ORDER BY login_time) AS rn
    FROM CTE_Groups
)
,CTE_Fixed
AS
(
    SELECT
        userID
        ,login_time
        ,ISNULL(logout_time, LEAD(logout_time) 
            OVER(PARTITION BY userID ORDER BY login_time)) AS fixed_logout_time
    FROM CTE_RN
    WHERE rn = 1
)
,CTE_FixedRN
AS
(
    SELECT
        userID
        ,login_time
        ,fixed_logout_time
        ,ROW_NUMBER() 
            OVER(PARTITION BY userID, fixed_logout_time ORDER BY login_time) AS rn
    FROM CTE_Fixed
)
,CTE_Sum
AS
(
    SELECT
        userID
        ,SUM(ISNULL(
            DATEDIFF(minute, login_time, fixed_logout_time),
            -1000000)) AS Duration
    FROM CTE_FixedRN
    WHERE rn = 1
    GROUP BY userID
)
SELECT
    userID
    ,CASE WHEN Duration < 0 THEN -1 ELSE Duration END AS Duration
FROM CTE_Sum
ORDER BY userID;

<强>结果

+--------+----------+
| userID | Duration |
+--------+----------+
| 222222 |       12 |
| 333333 |       -1 |
| 444444 |       10 |
+--------+----------+