TSQL Calculate Time duration between blocks of events

时间:2016-03-02 11:02:21

标签: tsql sql-server-2008-r2

SQL Server 2008r2

Can you help me calculate the time duration between blocks of events?

Example dataset

Id  NodeId  StartTime           NextId
16  87771   2016-02-01 11:01:00 17
17  87771   2016-02-01 11:02:00 18
18  87771   2016-02-01 11:03:00 19
19  87771   2016-02-01 11:05:00 NULL

27  87774   2016-02-01 08:43:00 28
28  87774   2016-02-01 08:44:00 29
29  87774   2016-02-01 08:46:00 30
30  87774   2016-02-01 08:47:00 NULL

40  87771   2016-02-01 11:52:00 41
41  87771   2016-02-01 11:53:00 42
42  87771   2016-02-01 11:55:00 NULL

72  87774   2016-02-01 10:07:00 73
73  87774   2016-02-01 10:08:00 74
74  87774   2016-02-01 10:09:00 75
75  87774   2016-02-01 10:11:00 76
76  87774   2016-02-01 10:13:00 NULL

I'm after the time difference in seconds between IDs 16 - 19 and 27 - 30 and 40 - 42 etc

So expected result

NodeId  Duration_Seconds
87771   240
87774   240
87771   180
87774   360

Thanks in advance.

4 个答案:

答案 0 :(得分:2)

假设事件块不与其他块交错,则解决方案是使用“窗口函数”识别每个块,以在NextId列(从下到上)中运行NULLS。< / p>

然后,您可以轻松找到每个块中的最小和最大StartTime值。

with blocks as (
    select *, 
        sum(case when NextId is null then 1 else 0 end) over (order by Id desc) as BlockId
    from #TEMP
)
select NodeId, DATEDIFF(second,min(StartTime), max(StartTime)) as Duration_Seconds 
from blocks
group by BlockId, NodeId
order by BlockId desc

我使用了填充从answer by Kamran

复制的#TEMP表的设置

答案 1 :(得分:0)

这是使用CURSOR yuk !!!

示例数据:

IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
    DROP TABLE #TEMP;

CREATE TABLE #TEMP(Id        INT,
                NodeId    INT,
                StartTime DATETIME,
                NextId    INT);

INSERT INTO #TEMP
VALUES
      (16, 87771, '2016-02-01 11:01:00', 17),
      (17, 87771, '2016-02-01 11:02:00', 18),
      (18, 87771, '2016-02-01 11:03:00', 19),
      (19, 87771, '2016-02-01 11:05:00', NULL),
      (27, 87774, '2016-02-01 08:43:00', 28),
      (28, 87774, '2016-02-01 08:44:00', 29),
      (29, 87774, '2016-02-01 08:46:00', 30),
      (30, 87774, '2016-02-01 08:47:00', NULL),
      (40, 87771, '2016-02-01 11:52:00', 41),
      (41, 87771, '2016-02-01 11:53:00', 42),
      (42, 87771, '2016-02-01 11:55:00', NULL),
      (72, 87774, '2016-02-01 10:07:00', 73),
      (73, 87774, '2016-02-01 10:08:00', 74),
      (74, 87774, '2016-02-01 10:09:00', 75),
      (75, 87774, '2016-02-01 10:11:00', 76),
      (76, 87774, '2016-02-01 10:13:00', NULL);

QUERY:

-- pull the data into a staging table with an extra column to hold GroupID 


IF OBJECT_ID('tempdb..#TEMP2') IS NOT NULL
    DROP TABLE #TEMP2;

SELECT *,
      CAST(NULL AS INT) AS groupID
INTO #temp2
FROM   #TEMP;

-- Unfortunately I cant see any other way except using a cursor put the results into groups. I really hate using cursors but...


DECLARE @NodeId  INT,
       @id      INT,
       @nextid  INT,
       @groupID INT = 1;

DECLARE group_Cursor CURSOR
FOR SELECT NodeId,
         Id,
         NextId
    FROM   #TEMP
    ORDER BY Id;

OPEN group_Cursor;

FETCH NEXT FROM group_Cursor INTO @NodeId,
                            @id,
                            @nextid;

WHILE @@FETCH_STATUS = 0
    BEGIN
       SELECT @NodeId,
            @id,
            @nextid;
       UPDATE A
        SET  A.groupID = @groupID
       FROM #temp2 AS A
       WHERE  A.NodeId = @NodeId
            AND A.Id = @id;
       FETCH NEXT FROM group_Cursor INTO @NodeId,
                                  @id,
                                  @nextid;
       IF @nextid IS NULL
          SELECT @groupID+=1;
    END;

CLOSE group_Cursor;

DEALLOCATE group_Cursor;

-- this is to resolve a bug where it gives groupID an extra increment if nextid IS NULL not sure why that's happening    
UPDATE t
  SET  t.groupID = t.groupID - 1
FROM #temp2 t
WHERE  nextid IS NULL;

SELECT t1.NodeId,
      SUM(DATEDIFF(SECOND, t1.StartTime, t2.StartTime))
FROM   #TEMP2 AS t1
      INNER JOIN #TEMP2 AS t2 ON t1.NodeId = t2.NodeId
                            AND t1.NextId = t2.Id
GROUP BY t1.NodeId,
        t1.groupID;

结果:

enter image description here

答案 2 :(得分:0)

另一种方法是通过递归:

--using the sample data in #temp as kindly provided by Kamran
    ;with cte as
    (select id,nodeid, starttime, 0 as timediff, nextid from #temp where nextid is null
    union all
    select t.id, t.nodeid, t.starttime, datediff(second,t.starttime,c.starttime)+c.timediff timediff, t.nextid
    from cte c 
    join #temp t on t.nextid=c.id
    )
    select nodeid, timediff from cte c1
    where not exists (select 1 from cte c2 where c2.nextid=c1.id)
    order by id

(我假设块内没有id间隙但块之间存在id间隙)

答案 3 :(得分:0)

我最终得到了这样的东西。

--using the sample data in #temp as kindly provided by Kamran
    SELECT
         C.NodeId
        ,SUM(DATEDIFF(SS, N.StartTime, C.StartTime ) )
    FROM
        #TEMP C
    CROSS APPLY
    (
        SELECT TOP 1
             NodeId
            ,StartTime
        FROM
            #TEMP M
        WHERE
            ID = C.ID - 1
    ) N
    WHERE
        N.NodeId = C.NodeId
    GROUP BY
            C.NodeId