Reputation: 155
I have a pending request to write a report for a temperature log application that I have written and I just can't seem to nail the query.
The application stores temperature logs. These logs are to be done 3 times a day at several different locations. Each log contains one or more areas, which contain one or more piece of equipment.
My report needs to show equipment (EquipmentStoreIDs) which have had a bad temperature for more than 3 log periods in a row for a given report date range. I need to show the "island", or date range of when the bad temps started and ended as well. Some caveats:
Here is my SQLFiddle. My results are not correct because the consecutive fails start on a time period that was actually missed and I cant figure out how to handle multiple logs for the same time period.
Any help is much appreciated!
Upvotes: 3
Views: 551
Reputation: 1520
Here's what I came up with.
To eliminate starting an island on a missed log, I compared the row's date with the first logged date we see per Store (minlog in the query below). If the row's date falls before that first log date, then we know it happened before we started recording, and I do not mark that with an error flag.
To handle multiple logs for the same time period, I added a Priority field in StagedRows, which we can use to grab only the first entry per Store/Datetime, prioritizing on a successful log.
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)
Upvotes: 1