2013-12-16 70 views
9

長時間潛行者,第一次發佈海報(和SQL初學者)。我的問題類似於這個SQL to find time elapsed from multiple overlapping intervals,除了我能夠使用CTE,UDF等,並且正在尋找更多的細節。SQL查找重疊時間段和子故障

在一臺大型設備上,我記錄了所有出現的故障。系統的不同子組件可能會出現故障,有些可能會完全脫機(完全停機=是),而另一些則不會(完成停機=否)。故障可能會在時間上重疊,如果故障尚未修復,可能沒有結束時間。

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      |---| 

我需要能夠制定出一個用戶定義的時間段,整個系統多久是全功能(無故障),多久的退化(一個或多個非完全中斷),以及如何長時間無法使用(一次或多次完全中斷)。我還需要能夠在任何給定的時間段內解決系統中的哪些故障。我打算在任何時候打開或關閉故障時創建一個「階段變更」表,但我堅持最好的方式來做到這一點 - 任何有關這個或更好的解決方案的幫助將不勝感激!

+3

只是想着在SQL中這樣做會讓我的頭部爆炸。 :)我可以問爲什麼不在應用程序代碼中執行此操作? –

+0

這可以在SQL中完成(搜索[間隙和孤島問題](https://www.simple-talk.com/sql/t-sql-programming/the-sql-of-gaps-and-islands- in-sequences /)),但它太複雜了。 – Andomar

+2

imho如果通過代碼來完成,那麼通過SQL來完成它會很有趣,並且你不得不經歷多年不同組件的故障,否則它很快就變得不可行。 – dendini

回答

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 
+0

我想過對此採用類似的方法,但我需要爲多個站點上的多個系統運行相同的代碼(總共大約60個),所以我認爲它會變得很慢。粒度很重要(系統重置時有些故障不到一分鐘),因此也必須使用較短的時間間隔,這使情況變得更糟。謝謝你的回答,但我非常感謝! – user3107032

+0

確實,這種解決方案可能無法很好地擴展,特別是在很長一段時間內。我也會說CTE方法可能也會遇到縮放問題,但是從事件數量來看,不一定是時間範圍。我會考慮說實話,並留意另一種方法 - 以適合您的需求爲準。 – user3056839

2

這不是一個完整的解決方案(我將它作爲一個練習:)),但應該說明基本技術。訣竅是創建一個狀態表(如你所說)。如果您爲「開始」事件記錄1,並且爲「結束」事件記錄-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計算運行總計的選項。

+0

謝謝大衛!花了我一段時間來解決這個問題,但我認爲我可以用它作爲基礎。 – user3107032