SQL计算仅包含2个特定值的两行之间的时间差

时间:2017-05-04 13:44:25

标签: sql sql-server

我有一个表格,其中包含有关移动设备上用户的状态更新;每次更新状态或更新其位置时,都会创建一个新行:

view_id  time_stamp           user           issue_event       original_value   new_value
-----------------------------------------------------------------------------------------
6040462  2017-03-20 02:00:43  DerekRoberts   Position updated  NULL             NULL
6040461  2017-03-20 02:04:01  JamesMorrison  state changed     Active           Paused
6040461  2017-03-20 02:08:33  JamesMorrison  Position updated  NULL             NULL
6040462  2017-03-20 02:20:42  DerekRoberts   Position updated  NULL             NULL
6040462  2017-03-20 02:32:29  DerekRoberts   state changed     Active           Paused
6040461  2017-03-20 02:34:11  JamesMorrison  state changed     Paused           Active
6040461  2017-03-20 02:36:22  JamesMorrison  Position updated  NULL             NULL
6040462  2017-03-20 02:52:47  DerekRoberts   Position updated  NULL             NULL
6040462  2017-03-20 03:01:03  DerekRoberts   state changed     Paused           Active

我要做的是找出每个用户保持在“暂停”状态的时间长度......这是[Paused]值与[Active]值之间的时间差[ new_value]列。为每个用户。

我以为我可以使用LEAD()为每个用户找到下一行,但是标记暂停时间结束的“活动”很少是下一行...它可能是任何数量的行进一步向下桌子。

到目前为止,我的查询如下:

;WITH UserPauseActivity AS
(SELECT [new_value],
 ShiftDate = CAST([time_stamp] as DATE),
 PauseStartUser = [user],
 PauseEndUser = LEAD([user], 1) OVER(ORDER BY [user], [time_stamp]),
 PauseStart = [time_stamp],
 PauseEnd = LEAD([time_stamp], 1) OVER(ORDER BY [user], [time_stamp]),
 PauseStartDate = CAST([time_stamp] AS DATE),
 PauseEndDate = CAST(LEAD([time_stamp], 1) OVER(ORDER BY [user], [time_stamp]) AS DATE)
FROM [SAFE].[dbo].[cc_shift_log_view])

SELECT PauseStartUser [user],
ShiftDate,
PauseStart,
PauseEnd,
DATEDIFF(minute, PauseStart, PauseEnd) IdleTime
FROM UserPauseActivity
WHERE [new_value] = 'Paused'
AND PauseEnd IS NOT NULL
AND PauseStartUser = PauseEndUser
AND PauseStartDate = PauseEndDate
AND PauseStartDate >= '2017-03-20 00:00:00' and PauseStartDate <= '2017-03-21 23:59:59'
ORDER BY ShiftDate, [user]

返回此内容:

user             ShiftDate    PauseStart           PauseEnd              IdleTime
---------------------------------------------------------------------------------
JamesMorrison    2017-03-20   2017-03-20 02:04:01  2017-03-20 02:08:33   4
DerekRoberts     2017-03-20   2017-03-20 02:32:29  2017-03-20 02:52:47   20

PauseStart值是正确的,但是PauseEnd不正确,是表中该用户的下一行,而不是包含相应“Active”值的下一行,该值表示其暂停持续时间的实际结束,所以任何帮助解决这个问题都会非常感激!

我正在使用MS SQL Server 2012。

3 个答案:

答案 0 :(得分:1)

假设唯一可能的状态变化来自Active - &gt;暂停 - &gt;有效......,您可以使用lead来获得所需的结果。

select usr,shiftDate,pause_start,pause_end,datediff(second,pause_start,pause_end)/60.0 as idleTime
from (select usr,cast(time_stamp as date) as shiftDate,time_stamp as pause_start
      ,lead(time_stamp) over(partition by usr order by time_stamp) as pause_end
      ,original_value,new_value
      from t
      where issue_event='state changed'
     ) t 
where original_value='Active' and new_value='Paused'    

答案 1 :(得分:0)

您可以使用pivot并获得此结果,假设您只有一个从活动状态更改为暂停并暂停为活动状态,如下所示:

select view_id, [user], [1] as [PauseStart], [2] as [PauseEnd], IdleTime = Datediff(MINUTE, [1],[2]) from (
select view_id, time_stamp, [user], RowN = Row_Number() over (partition by [user] order by time_stamp) from #youruser 
where issue_event = 'state changed   '
) a
pivot (max(time_stamp) for RowN in ([1],[2])) p

输出:

+---------+---------------+-------------------------+-------------------------+----------+
| view_id |     user      |       PauseStart        |        PauseEnd         | IdleTime |
+---------+---------------+-------------------------+-------------------------+----------+
| 6040462 | DerekRoberts  | 2017-03-20 02:32:29.000 | 2017-03-20 03:01:03.000 |       29 |
| 6040461 | JamesMorrison | 2017-03-20 02:04:01.000 | 2017-03-20 02:34:11.000 |       30 |
+---------+---------------+-------------------------+-------------------------+----------+

答案 2 :(得分:0)

另一种可能性是带有子查询的OUTER APPLY(下面的示例)。如果您不想捕捉尚未完成的暂停,请将OUTER APPLY更改为CROSS APPLY。

CREATE TABLE #Table (
    view_id int,
    time_stamp DateTime,
    [user] nvarchar(50),
    issue_event nvarchar(50),
    original_value nvarchar(30),
    new_value nvarchar(40))

INSERT INTO #Table
VALUES
(6040462,'2017-03-20 02:00:43','DerekRoberts','Position updated',NULL,NULL)
,(6040461,'2017-03-20 02:04:01','JamesMorrison','state changed','Active','Paused')
,(6040461,'2017-03-20 02:08:33','JamesMorrison','Position updated',NULL,NULL)
,(6040462,'2017-03-20 02:20:42','DerekRoberts','Position updated',NULL,NULL)
,(6040462,'2017-03-20 02:32:29','DerekRoberts','state changed','Active','Paused')
,(6040461,'2017-03-20 02:34:11','JamesMorrison','state changed','Paused','Active')
,(6040461,'2017-03-20 02:36:22','JamesMorrison','Position updated',NULL,NULL)
,(6040462,'2017-03-20 02:52:47','DerekRoberts','Position updated',NULL,NULL)
,(6040462,'2017-03-20 03:01:03','DerekRoberts','state changed','Paused','Active')
,(6040462,'2017-03-21 03:32:29','DerekRoberts','state changed','Active','Paused')
,(6040462,'2017-03-21 04:05:03','DerekRoberts','state changed','Paused','Active')
,(6040461,'2017-03-22 02:04:01','JamesMorrison','state changed','Active','Paused')


SELECT
    *
FROM
    #Table

SELECT
    [user], 
    CONVERT(nvarchar(10), pausedEvent.time_stamp, 102) ShiftDate, 
    pausedEvent.time_stamp PauseStart,
    activatedEvent.time_stamp PauseEnd,
    DATEDIFF(minute, pausedEvent.time_stamp, activatedEvent.time_stamp) IdleTime
FROM
    #Table pausedEvent   
    OUTER APPLY (
        SELECT TOP 1 
            activatedEvent.time_stamp
        FROM 
            #Table activatedEvent
        WHERE 
            activatedEvent.issue_event = 'state changed'
            and activatedEvent.original_value = 'Paused'
            and activatedEvent.new_value = 'Active'
            and activatedEvent.time_stamp > pausedEvent.time_stamp
            and activatedEvent.[user] = pausedEvent.[user]
        ORDER BY time_stamp
    ) activatedEvent
WHERE
    pausedEvent.issue_event = 'state changed'
    and pausedEvent.original_value = 'Active'
    and pausedEvent.new_value = 'Paused'

DROP TABLE #Table

请注意,各种选项的性能可能会有所不同,具体取决于可用的索引(我不确定这会有多快)。