查询以汇总未同时运行的后续持续时间

时间:2019-05-17 17:15:11

标签: sql sql-server tsql

我有一个查询,可以查询事件延迟的开始和结束时间,并计算CTE中的长度。单个事件可能会有多个延迟,但是由于用户错误,延迟可能同时发生。我想编写一个查询,将所有连续的延迟加在一起,包括按最早开始的延迟计算的并发运行的单个持续时间。

要获取数据表:         使用[tempdb]

GO

CREATE TABLE [dbo].[tblDelay](
        [Delay_ID] [INT] NOT NULL,
        [EventID] [INT] NOT NULL,
        [D_Time] [Datetime] NOT NULL,
        [D_EndTime] [Datetime]
        )
GO

INSERT INTO tblDelay VALUES(1,1,'10:00','10:01');
INSERT INTO tblDelay VALUES(2,1,'10:05','10:06');
INSERT INTO tblDelay VALUES(3,1,'10:05:01','10:06');
INSERT INTO tblDelay VALUES(4,2,'10:00','10:01');
INSERT INTO tblDelay VALUES(5,2,'10:04','10:05');
INSERT INTO tblDelay VALUES(6,2,'10:06','10:07');
INSERT INTO tblDelay VALUES(7,2,'10:06:01','10:07');
INSERT INTO tblDelay VALUES(8,2,'10:10','10:12');
INSERT INTO tblDelay VALUES(8,2,'10:10:01','10:12');

我试图通过使用LEFT self-JOIN选择立即的下一个延迟,然后总结结果来做到这一点。我可以执行多个自联接,但是我希望它可以自动扩展,而不仅仅是测试一定数量的延迟。下面的代码有效:

WITH DelLength AS
(
    SELECT 
        EventID, Delay_ID,
        D_time, d_EndTime,
        CAST(CAST(DATEDIFF(SECOND, D_Time, D_EndTime) AS DECIMAL(10, 2)) / 60 AS DECIMAL(10, 2)) AS DelLength
    FROM 
        tblDelay d
)
SELECT
    dl1.EventID,
    dl1.D_Time,
    dl1.D_EndTime,
    (dl1.DelLength + dl2.DelLength + dl4.DelLength) AS Total
FROM
    DelLength AS dl1
INNER JOIN 
    tblEvent e ON dl1.EventID = e.EventID
LEFT JOIN 
    DelLength AS dl2 ON dl1.EventID = dl2.EventID
                     AND dl2.D_Time = (SELECT TOP 1 dl3.D_Time
                                       FROM DelLength dl3
                                       WHERE dl3.EventID = dl1.EventID
                                         AND dl3.D_Time > dl1.D_EndTime
                                       ORDER BY dl3.D_Time)
LEFT JOIN 
    DelLength dl4 ON dl4.EventID = dl1.EventID
                  AND dl4.D_Time = (SELECT TOP 1 dl5.D_Time
                                    FROM DelLength as dl5
                                    WHERE dl5.EventID = dl1.EventID
                                      AND dl5.D_Time > dl2.D_EndTime
                                    ORDER BY dl5.D_Time)

为每个事件以及记录的第一,第二和第三后续延迟之和(如存在)产生一条记录。但是我要为无限多个后续延迟进行编码。

谢谢!

2 个答案:

答案 0 :(得分:1)

您正在寻找一种解决方案,以按时间顺序虚拟化岛组。类似于以下查询的内容可能会有所帮助。虚拟化分组并获取每组的最小值和最大值,单个记录或非岛屿记录在下降时将按顺序排序,而一个岛内的所有记录将与该组中的第一条记录具有相同的顺序。

;WITH Markers AS
(
    SELECT
        *,
        VirtualGroupID = SUM(IsNewGroup) OVER (ORDER BY D_Time ROWS UNBOUNDED PRECEDING)
    FROM
    (        
        SELECT
            EventID, Delay_ID,
            D_time, d_EndTime,
            IsNewGroup = CASE WHEN ISNULL(LAG(EventID) OVER (ORDER BY D_Time),EventID)<>EventID THEN 1 ELSE 0 END
        FROM
            tblDelay d
    )AS X
)

SELECT 
    EventID = MAX(EventId),
    d_Time = MIN(d_Time),
    d_EndTime = MAX(d_EndTime)
FROM 
    Markers
GROUP BY
    VirtualGroupID

答案 1 :(得分:0)

非常感谢Ross使我走上正确的轨道。我发现您的答案是我所要查找方法的90%,只需在最里面的子查询中对窗口表达式进行详细说明即可对其进行补充。为了防止引入从上一个结束之前开始的延迟,我扩展了代码:

WITH Markers AS
(
    SELECT
        *,
        VirtualGroupID = SUM(IsNewGroup) OVER (ORDER BY D_Time ROWS UNBOUNDED PRECEDING)
    FROM
    (        
        SELECT
            EventID, Delay_ID,
            D_time, d_EndTime,
            IsNewGroup = CASE 
                        WHEN        
ISNULL(LAG(EventID) OVER (ORDER BY EventID, D_Time),EventID)<>EventID 
                        OR          
(LAG(EventID) OVER (ORDER BY EventID, D_Time) = EventID 
AND D_Time > LAG(D_EndTime) OVER (ORDER BY EventID,D_Time)) 
                        THEN 1 
                        ELSE 0 END
        FROM
            tblDelay d
    )AS X

)

SELECT 
    EventID = MAX(EventId),
    d_Time = MIN(d_Time),
    d_EndTime = MAX(d_EndTime)
FROM 
    Markers

GROUP BY
    VirtualGroupID
    ORDER BY EventID

我认为这是正确的。

再次感谢!