SQL查找重叠的时间段和子故障

时间:2013-12-16 11:26:40

标签: sql datetime overlapping

长时间跟踪者,第一次海报(和SQL初学者)。我的问题类似于这个SQL to find time elapsed from multiple overlapping intervals,除了我能够使用CTE,UDF等,我正在寻找更多细节。

在一台大型设备上,我记录了所有出现的故障。系统的不同子组件可能出现故障,有些可能完全脱机(完全停机=是),而其他子组件则没有(完全停机=否)。故障可能会在时间上重叠,如果故障尚未修复,故障可能没有结束时间。

Outage_ID     StartDateTime     EndDateTime     CompleteOutage
1             07:00 3-Jul-13    08:55 3-Jul13   Yes
2             08:30 3-Jul-13    10:00 4-Jul13   No
3             12:00 4-Jul-13                    No
4             12:30 4-Jul13     12:35 4-Jul-13  No


1 |---------|
2    |---------|
3                 |--------------------------------------------------------------
4                      |---|

我需要能够在用户定义的时间段内完成工作,整个系统完全正常运行多长时间(无故障),退化多长时间(一个或多个非完全中断)以及无法运行多长时间(一个或更完整的中断)。我还需要能够在任何给定的时间段内解决系统中出现的故障。我想在打开或关闭故障的任何时候创建一个“Stage Change”表,但我仍然坚持这样做的最佳方式 - 对这个或更好的解决方案的任何帮助都将不胜感激!

2 个答案:

答案 0 :(得分:2)

这不是一个完整的解决方案(我将其留作练习:))但应该说明基本技术。诀窍是创建一个状态表(如你所说)。如果您为"开始"事件和-1为"结束"事件然后事件日期/时间顺序中的运行总计为您提供该特定事件日期/时间的当前状态。下面的SQL是T-SQL,但应该很容易适应您正在使用的任何数据库服务器。

以部分中断数据为例:

DECLARE @Faults TABLE (
    StartDateTime DATETIME NOT NULL,
    EndDateTime DATETIME NULL
)
INSERT INTO @Faults (StartDateTime, EndDateTime)
    SELECT '2013-07-03 08:30', '2013-07-04 10:00'
    UNION ALL SELECT '2013-07-04 12:00', NULL
    UNION ALL SELECT '2013-07-04 12:30', '2013-07-04 12:35'

-- "Unpivot" the events and assign 1 to a start and -1 to an end
;WITH FaultEvents AS (
    SELECT *, Ord = ROW_NUMBER() OVER(ORDER BY EventDateTime)
        FROM (
            SELECT EventDateTime = StartDateTime, Evt = 1
                FROM @Faults
            UNION ALL SELECT EndDateTime, Evt = -1
                FROM @Faults
                WHERE EndDateTime IS NOT NULL
        ) X
)
-- Running total of Evt gives the current state at each date/time point
, FaultEventStates AS (
    SELECT A.Ord, A.EventDateTime, A.Evt, [State] = (SELECT SUM(B.Evt) FROM FaultEvents B WHERE B.Ord <= A.Ord)
        FROM FaultEvents A
)
SELECT StartDateTime = S.EventDateTime, EndDateTime = F.EventDateTime
    FROM FaultEventStates S
        OUTER APPLY (
            -- Find the nearest transition to the no-fault state
            SELECT TOP 1 *
                FROM FaultEventStates B
                WHERE B.[State] = 0
                    AND B.Ord > S.Ord
                ORDER BY B.Ord
        ) F
    -- Restrict to start events transitioning from the no-fault state
    WHERE S.Evt = 1 AND S.[State] = 1

如果您使用的是SQL Server 2012,则可以选择使用windowing function计算运行总计。

答案 1 :(得分:1)

以下是实现此工作的粗略指南。它将与日期间隔表和15分钟的间隔表进行比较。然后它会将中断事件(每个间隔1个事件)相加,但如果完全中断则不会将部分中断加起来。

如果需要,您可以使用更精细的时间间隔,我选择15分钟来提高编码速度。

我已经设置了日期间隔表“CAL.t_Calendar”,因此您需要创建自己的一个来运行此代码。

请注意,这并不代表您应该使用的实际代码。它只是作为一个演示,并指出你可能的方向......

编辑我刚刚意识到我没有考虑到空结束日期。代码将需要修改以检查NULL endDates并使用@EndDate或GETDATE()如果@EndDate是将来

            --drop table ##Events
            CREATE TABLE #Events (OUTAGE_ID INT IDENTITY(1,1) PRIMARY KEY 
                                    ,StartDateTime datetime
                                    ,EndDateTime datetime
                                    , completeOutage bit)

            INSERT INTO #Events VALUES ('2013-07-03 07:00','2013-07-03 08:55',1),('2013-07-03 08:30','2013-07-04 10:00',0)
                                        ,('2013-07-04 12:00',NULL,0),('2013-07-04 12:30','2013-07-04 12:35',0)


            --drop table #FiveMins
            CREATE TABLE #FiveMins (ID int IDENTITY(1,1) PRIMARY KEY, TimeInterval Time)


            DECLARE @Time INT = 0

            WHILE @Time <= 1410 --number of 15 min intervals in day * 15 

            BEGIN

                INSERT INTO #FiveMins SELECT DATEADD(MINUTE , @Time,  '00:00')

                SET @Time = @Time + 15

            END

            SELECT * from #FiveMins



            DECLARE @StartDate DATETIME = '2013-07-03'
            DECLARE @EndDate DATETIME = '2013-07-04 23:59:59.999'


            SELECT SUM(FullOutage) * 15 as MinutesFullOutage
                    ,SUM(PartialOutage) * 15 as MinutesPartialOutage
                    ,SUM(NoOutage) * 15  as MinutesNoOutage
            FROM
            (
                SELECT DateAnc.EventDateTime
                        , CASE WHEN COUNT(OU.OUTAGE_ID) > 0 THEN 1 ELSE 0 END AS FullOutage
                        , CASE WHEN COUNT(OU.OUTAGE_ID) = 0 AND COUNT(pOU.OUTAGE_ID) > 0 THEN 1 ELSE 0 END AS PartialOutage
                        , CASE WHEN COUNT(OU.OUTAGE_ID) > 0 OR  COUNT(pOU.OUTAGE_ID) > 0 THEN 0 ELSE 1 END AS NoOutage
                FROM 
                (
                    SELECT CAL.calDate + MI.TimeInterval AS EventDateTime
                    FROM CAL.t_Calendar CAL

                    CROSS JOIN #FiveMins MI

                    WHERE CAL.calDate BETWEEN @StartDate AND @EndDate 
                ) DateAnc

                LEFT JOIN #Events OU
                ON DateAnc.EventDateTime BETWEEN OU.StartDateTime AND OU.EndDateTime
                AND OU.completeOutage = 1

                LEFT JOIN #Events pOU
                ON DateAnc.EventDateTime BETWEEN pOU.StartDateTime AND pOU.EndDateTime
                AND pOU.completeOutage = 0

                GROUP BY DateAnc.EventDateTime
            ) AllOutages