陷入涉及时间段的间隙和岛屿查询

时间:2015-01-16 21:07:09

标签: sql sql-server database gaps-and-islands

我有一个待处理的请求,要为我编写的温度日志应用程序编写报告,我似乎无法确定查询。

该应用程序存储温度日志。这些日志每天要在几个不同的位置进行3次。每个日志包含一个或多个区域,其中包含一个或多个设备。

我的报告需要显示设备(EquipmentStoreID),这些设备在给定的报告日期范围内连续超过3个对数周期的温度很差。我需要显示"岛",或者坏时间开始和结束时的日期范围。一些警告:

  • 如果错过了一段时间,应将其视为温度不佳。然而,"岛"失败的临时值不应该从错过的日志开始。
  • 在给定时间段内,可以通过相同的位置完成多个日志,在这种情况下,良好的温度将胜过任何不良的日志。

这是我的SQLFiddle。我的结果不正确,因为在实际错过的时间段内连续失败启动,我无法弄清楚如何处理同一时间段内的多个日志。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

这就是我想出的。

为了消除错过日志上的岛屿,我将行的日期与我们在每个商店看到的第一个记录日期(下面查询中的minlog)进行了比较。如果行的日期在第一个日志日期之前,那么我们知道它发生在我们开始记录之前,并且我没有用错误标记标记它。

为了处理同一时间段内的多个日志,我在StagedRows中添加了一个Priority字段,我们可以使用它来获取每个Store / Datetime的第一个条目,优先考虑成功的日志。

WITH TargetLogs 
     AS (SELECT le.*, 
                CONVERT(DATETIME, CONVERT(DATE, lh.StartDateTime)) as Date,
                lh.TimePeriodID, 
                lh.StartDateTime 
         FROM   [dbo].[LogEquipment] le 
                JOIN [dbo].[LogArea] la 
                  ON le.LogAreaID = la.LogAreaID 
                JOIN [dbo].[LogHeader] lh 
                  ON lh.LogHeaderID = la.LogHeaderID 
         WHERE  lh.StartDateTime Between CAST('2015-01-14' AS DateTime)
         AND CAST('2015-01-16' AS DateTime)
        ), 

     Dates --Generate date range
     AS (SELECT CAST('2015-01-14' AS DateTime) 'date' 
         UNION ALL 
         SELECT Dateadd(dd, 1, t.date) 
         FROM   Dates t 
         WHERE  Dateadd(dd, 1, t.date) <= CAST('2015-01-16' AS DateTime)), 

     DesiredDatesAndTimePeriods --Generate datetimes for all timeperiods between date range
     AS (SELECT DISTINCT tp.TimePeriodID, 
                tp.TimeDescription, 
                tp.StartTime,
                d.Date,
                d.Date + CONVERT(DATETIME, tp.StartTime) AS LogStartDateTime,
                le.EquipmentStoreID
         FROM   dbo.TimePeriod tp 
                CROSS JOIN Dates d 
                CROSS JOIN LogEquipment le 
         WHERE  tp.IsActive = 1), 

     StagedRows 
     AS (SELECT * FROM
     (
     SELECT d.LogStartDateTime, 
            d.EquipmentStoreID, 
            t.LogEquipmentID, 
            t.CorrectiveAction, 
            CASE WHEN minlog.MinStartDateTime <= d.LogStartDateTime 
                AND (t.LogEquipmentID IS NULL OR CorrectiveAction IS NOT NULL)  
                THEN 1 ELSE 0 END AS FailedFlag,
            ROW_NUMBER() OVER (PARTITION BY d.Date, d.TimePeriodID 
                 ORDER BY CASE WHEN CorrectiveAction IS NULL THEN 0 ELSE 1 END) 
                 AS Priority
     FROM DesiredDatesAndTimePeriods d
         LEFT OUTER JOIN TargetLogs t 
             on d.Date = t.Date AND d.TimePeriodId = t.TimePeriodId
         LEFT OUTER JOIN (SELECT EquipmentStoreId, MIN(StartDateTime) as MinStartDateTime FROM TargetLogs GROUP BY EquipmentStoreId) minlog 
             on d.EquipmentStoreID = minlog.EquipmentStoreID 
    ) dt WHERE Priority = 1) 

SELECT EquipmentStoreID,
          Count(*) AS ConsecutiveFails, 
       Start_date = Min(LogStartDateTime), 
       Stop_date = Max(LogStartDateTime) 
FROM   (SELECT EquipmentStoreID,
                        FailedFlag, 
               LogStartDateTime, 
               ROW_NUMBER() 
                 OVER ( 
                   ORDER BY EquipmentStoreID, LogStartDateTime) - ROW_NUMBER() 
                                                  OVER ( 
                                                    PARTITION BY EquipmentStoreID, FailedFlag 
                                                    ORDER BY EquipmentStoreID, LogStartDateTime) 
               grp 
        FROM   StagedRows) A 
GROUP  BY EquipmentStoreID, FailedFlag, 
          grp 
HAVING FailedFlag = 1 
       AND Count(*) > 3 
ORDER  BY Min(LogStartDateTime)