计算实际停机时间,忽略日期/时间的重叠。

3

我正在尝试从存储在表中的数据中计算各种应用程序的实际停机时间。

目前,我只是计算DowntimeStart和DowntimeEnd之间的差异,这显示在DowntimeMinutes中。

问题是,如果由于单独的组件出现故障而存在时间交叉,则应忽略重叠部分并计算总时间。

我期望的结果显示在Expected列中。

有什么想法可以编写查询来实现这一点吗?

Application         DowntimeStart           DowntimeEnd              DowntimeMinutes    Expected
Application Demo    2014-11-20 17:31:01.467 2014-11-20 18:01:01.243  30                 30
Application Demo    2014-11-28 17:59:00.987 2014-11-28 18:09:02.167  10                 26
Application Demo    2014-11-28 18:00:01.403 2014-11-28 18:25:01.443  25                 0
Application Demo    2014-11-29 19:13:08.580 2014-11-30 05:30:01.763  617                617
Application Demo    2014-11-30 01:55:01.953 2014-11-30 03:54:01.730  119                0

我已经查看并调查了这些选项,但它们无法实现以下目标: 查找总分钟数(忽略重叠)(将基于游标的答案转换为CTE) 从多个重叠区间中查找经过的时间的SQL http://www.experts-exchange.com/Database/MS-SQL-Server/SQL_Server_2008/Q_28169653.html 基于日期计算经过的时间 https://forums.teradata.com/forum/database/duration-calculation-between-dates-which-has-overlap-with-other-date-ranges-for-same
2个回答

0

使用新的测试用例更新

这里有一种技术可以计算出唯一的停机时间,然后将它们与导致停机的初始停机时间对齐,以使实际值和预期值相匹配。

DECLARE @Downtime TABLE (
    ID INT PRIMARY KEY NOT NULL IDENTITY(1,1),
    Application VARCHAR(25), 
    DowntimeStart DATETIME,
    DowntimeEnd DATETIME,
    Expected INT
)

INSERT @Downtime (Application, DowntimeStart, DowntimeEnd, Expected) VALUES -- Act/Exp
    ('Application Demo', '2014-11-20 17:31:01.467', '2014-11-20 18:01:01.243', 30) -- 30/30
    ,('Application Demo', '2014-11-28 17:59:00.987', '2014-11-28 18:09:02.167', 26) -- 10/26
    ,('Application Demo', '2014-11-28 18:00:01.403', '2014-11-28 18:25:01.443', 0) -- 25/0
    ,('Application Demo', '2014-11-29 19:13:08.580', '2014-11-30 05:30:01.763', 617) -- 617/617
    ,('Application Demo', '2014-11-30 01:55:01.953', '2014-11-30 03:54:01.730', 0)
    ,('Application Demo 2', '2014-12-19 23:09:01.303', '2014-12-22 09:43:01.397', 3514)
    ,('Application Demo 2', '2014-12-19 23:09:01.303', '2014-12-22 09:43:01.397', 0)
    ,('Application Demo 2', '2014-12-19 23:09:01.303', '2014-12-22 09:43:01.397', 0) 
    ,('Application Demo 2', '2014-12-19 23:09:01.303', '2014-12-22 09:43:01.397', 0)
    ,('Application Demo 2', '2014-12-19 23:09:01.303', '2014-12-22 09:43:01.397', 0) 

SELECT
    Downtimes.Application,
    Downtimes.DowntimeStart,
    Downtimes.DowntimeEnd,
    Downtimes.Expected,
    COALESCE(Actual, 0) AS Actual
FROM @Downtime Downtimes
    LEFT OUTER JOIN (
        SELECT DISTINCT
            D1.Application,
            MIN(CASE WHEN D1.DowntimeStart < D2.DowntimeStart THEN D1.ID ELSE D2.ID END) AS [ID],
            MIN(CASE WHEN D1.DowntimeStart < D2.DowntimeStart THEN D1.DowntimeStart ELSE D2.DowntimeStart END) AS [DowntimeStart],
            MAX(CASE WHEN D1.DowntimeEnd > D2.DowntimeEnd THEN D1.DowntimeEnd ELSE D2.DowntimeEnd END) AS [DowntimeEnd],
            DATEDIFF(MINUTE,
                MIN(CASE WHEN D1.DowntimeStart < D2.DowntimeStart THEN D1.DowntimeStart ELSE D2.DowntimeStart END),
                MAX(CASE WHEN D1.DowntimeEnd > D2.DowntimeEnd THEN D1.DowntimeEnd ELSE D2.DowntimeEnd END)) AS Actual
        FROM @Downtime D1
            INNER JOIN @Downtime D2
                ON D1.Application = D2.Application
                    AND (D1.DowntimeStart BETWEEN D2.DowntimeStart AND D2.DowntimeEnd
                        OR D2.DowntimeStart BETWEEN D1.DowntimeStart AND D1.DowntimeEnd)
        GROUP BY
            D1.Application,
            D1.DowntimeStart
    ) Outages
        ON Outages.ID = Downtimes.ID

这将产生所需的输出:

Application               DowntimeStart           DowntimeEnd             Expected    Actual
------------------------- ----------------------- ----------------------- ----------- -----------
Application Demo          2014-11-20 17:31:01.467 2014-11-20 18:01:01.243 30          30
Application Demo          2014-11-28 17:59:00.987 2014-11-28 18:09:02.167 26          26
Application Demo          2014-11-28 18:00:01.403 2014-11-28 18:25:01.443 0           0
Application Demo          2014-11-29 19:13:08.580 2014-11-30 05:30:01.763 617         617
Application Demo          2014-11-30 01:55:01.953 2014-11-30 03:54:01.730 0           0
Application Demo 2        2014-12-19 23:09:01.303 2014-12-22 09:43:01.397 3514        3514
Application Demo 2        2014-12-19 23:09:01.303 2014-12-22 09:43:01.397 0           0
Application Demo 2        2014-12-19 23:09:01.303 2014-12-22 09:43:01.397 0           0
Application Demo 2        2014-12-19 23:09:01.303 2014-12-22 09:43:01.397 0           0
Application Demo 2        2014-12-19 23:09:01.303 2014-12-22 09:43:01.397 0           0

这里有一个小的数据验证问题:最后一行的开始时间在前一行的开始和结束时间之间,因此应该影响两个持续时间。 - Jaaz Cole
插入 #TABLENAME (Application, DowntimeStart, DowntimeEnd, Expected) 值 - ('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',3514) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0)- ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) - Philip
你的表是否有可用于更改的主键? - Jason W
是的,它是一个名为"ID"的整型变量。 - Philip
如果您有一个应用程序表,则将其与您的停机时间表进行左外连接,然后使用COALESCE或ISNULL将停机时间值与0合并(即:COALESCE(DATEDIFF(...), 0)),如果该值为NULL。 - Jason W
显示剩余3条评论

0

我曾经遇到过类似的问题,在我的如何合并时间块?提问中得到了答案。

在你的情况下,可以通过使用 top 1 self outer apply 来获取重叠部分,然后使用重叠部分的开始时间或正常记录的结束时间(如果为空)作为结束时间来完成。

CREATE TABLE Downtime (
    Application VARCHAR(25), 
    DowntimeStart DATETIME,
    DowntimeEnd DATETIME,
    Expected INT
)

INSERT Downtime (Application, DowntimeStart, DowntimeEnd, Expected) VALUES -- Act/Exp
    ('Application Demo', '2014-11-20 17:31:01.467', '2014-11-20 18:01:01.243', 30) -- 30/30
    ,('Application Demo', '2014-11-28 17:59:00.987', '2014-11-28 18:09:02.167', 26) -- 10/26
    ,('Application Demo', '2014-11-28 18:00:01.403', '2014-11-28 18:25:01.443', 0) -- 25/0
    ,('Application Demo', '2014-11-29 19:13:08.580', '2014-11-30 05:30:01.763', 617) -- 617/617
    ,('Application Demo', '2014-11-30 01:55:01.953', '2014-11-30 03:54:01.730', 0)


SELECT
    Records.Application, Records.DowntimeStart, Records.DowntimeEnd, Records.Expected
  , DATEDIFF(minute, Records.DowntimeStart, COALESCE(Overlap.DowntimeStart, Records.DowntimeEnd)) AS Actual
--  , Overlap.Application, Overlap.DowntimeStart, Overlap.DowntimeEnd -- For Verification Purposes
FROM Downtime Records
  OUTER APPLY (
    SELECT TOP 1 Overlap.Application, Overlap.DowntimeStart, Overlap.DowntimeEnd
    FROM Downtime Overlap
    WHERE Records.Application = Overlap.Application
      AND Overlap.DowntimeStart > Records.DowntimeStart
      AND Overlap.DowntimeStart BETWEEN Records.DowntimeStart AND Records.DowntimeEnd
    ORDER BY Overlap.DowntimeStart
    ) Overlap

这里是SQLFiddle的解决方案。


非常感谢Jaaz,我没有考虑到一个问题,直到我测试了所有数据。如果你有以下内容,请参阅下一条消息,它会返回相同的值。是否有办法修改查询以考虑这一点? - Philip
插入 #TABLENAME (Application, DowntimeStart, DowntimeEnd, Expected) 值 - ('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',3514) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0)- ,('Application Demo 2','2014-12-19 23:09:01.303','2014-12-22 09:43:01.397',0) - Philip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接