2016-04-28 3 views
3

У меня есть таблица, которая отслеживает Datetime of Incident_ID, созданных для определенных Device_ID, и я пытаюсь найти способ отслеживания хронических проблем в течение ряда дат. Определение хронической проблемы - это любой Device_ID, у которого было 3 или более идентификаторов Incident_ID, созданных за последние 5 дней. Мне нужно иметь возможность выполнять поиск по различным датам (в основном ежемесячно).MS SQL 2014 - количество повторяющихся осколков по диапазону дат

Учитывая таблицу:

IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL 
DROP TABLE #temp 

CREATE TABLE #temp 
    (Device_ID INT, 
    Incident_ID INT, 
    Incident_Datetime DATETIME) 

INSERT INTO #temp 
VALUES 
    (2,1001,'2016-02-01'), 
    (3,1002,'2016-02-02'), 
    (2,1003,'2016-02-09'), 
    (2,1004,'2016-02-10'), 
    (5,1005,'2016-02-12'), 
    (2,1006,'2016-02-13'), 
    (5,1007,'2016-02-14'), 
    (5,1008,'2016-02-15'), 
    (3,1009,'2016-02-18'), 
    (3,1010,'2016-02-19'), 
    (3,1011,'2016-02-20'), 
    (5,1012,'2016-02-21'), 
    (3,1013,'2016-03-18'), 
    (3,1014,'2016-03-19'), 
    (3,1015,'2016-03-20'); 

Желаемый результат хронических проблем для 02-2016 является:

Device_ID Incident_ID Incident_Datetime 
2   1003   2/9/16 0:00 
2   1004   2/10/16 0:00 
2   1006   2/13/16 0:00 
3   1009   2/18/16 0:00 
3   1010   2/19/16 0:00 
3   1011   2/20/16 0:00 
5   1005   2/12/16 0:00 
5   1007   2/14/16 0:00 
5   1008   2/15/16 0:00 

Я попытался следующий запрос, который показывает мне количество восходящую инцидентов и позволяет мне найти те устройства-устройства, у которых были хронические проблемы, но мне трудно изолировать все инциденты, которые составляют хронический вопрос, исключая те выбросы, которые произошли за пределами 3-дневного диапазона.

SELECT c.Device_ID, c.Incident_ID, c.Incident_Datetime, 
    (SELECT COUNT(*) 
    FROM #temp AS t 
    WHERE 
     c.Device_ID = t.Device_ID 
     AND 
     t.Incident_Datetime BETWEEN DATEADD(DAY,-5,c.Incident_Datetime) AND c.Incident_Datetime) AS Incident_Count 
FROM #temp AS c 
WHERE 
    c.Incident_Datetime >= '2016-02-01' 
    AND 
    c.Incident_Datetime < '2016-03-01' 
ORDER BY 
    Device_ID, Incident_Datetime 

ответ

1

Это, вероятно, не совсем так хорошо, как ответ Джейка, но вот альтернативное решение, которое может работать:

 WITH cte AS 
     (
      SELECT tmp.Device_ID, tmp.Incident_Datetime FROM #temp AS tmp 
      CROSS APPLY 
      ( 
      SELECT Device_ID 
      FROM #temp AS t 
      WHERE tmp.Device_ID = t.Device_ID AND t.Incident_Datetime BETWEEN DATEADD(d,-5,tmp.Incident_Datetime) AND tmp.Incident_Datetime 
      GROUP BY Device_ID HAVING COUNT(Incident_ID) >= 3 
     ) p 
      WHERE tmp.Incident_Datetime BETWEEN '02-01-2016' AND '03-01-2016' 
     )   

     SELECT f.* 
     FROM #temp f 
     INNER JOIN cte 
     ON f.Device_ID = cte.Device_ID 
     WHERE f.Incident_Datetime BETWEEN DATEADD(d,-5,cte.Incident_Datetime) AND cte.Incident_Datetime 
     GROUP BY f.Device_ID, f.Incident_ID, f.Incident_Datetime 
     ORDER BY f.Device_ID, f.Incident_Datetime 
+0

Удивительный, это работает и был именно альтернативным подходом, который мне нужен, чтобы преодолеть ментальное препятствие, которое этот вопрос дал мне. Мне нужно будет поработать над CROSS APPLY, поскольку они не то, что я часто использую, но это делает трюк! – Jericho

+0

Это был первый раз, когда я когда-либо использовал его на самом деле. Это дало мне много идей о том, как использовать его в будущем. Поэтому я ценю умственные упражнения. :) –

0

Как об этом ...

DECLARE @StartDate datetime, @EndDate datetime 
SET @StartDate='2016-02-01' 
SET @EndDate='2016-03-01' 

SELECT c.Device_ID, c.Incident_ID, c.Incident_DateTime FROM #temp c 
INNER JOIN (SELECT t.Device_ID, Count(*) FROM #temp 
    WHERE t.Incident_DateTime BETWEEN DATEADD(dd, -3, c.Incident_DateTime) AND DATEADD(dd, +3, c.Incident_DateTime) 
    GROUP BY t.Device_ID 
    HAVING Count(*) > 2)) t ON c.Device_ID = t.Device_ID 
AND c.Incident_DateTime BETWEEN @StartDate AND @EndDate 
ORDER BY 
c.Device_ID, c.Incident_Datetime 
+0

Я не в состоянии получить этот один, чтобы работать. Похоже, есть дополнительные скобки после HAVING COUNT (*), но тогда он не может привязать t. и c. идентификаторы для запроса внутреннего соединения – Jericho

0

Вот способ вывести бегущий инцидентов в течение п дней всего:

with 
incidents as (
    select * from #temp cross apply (
    select incident_datetime, 1 union all 
    select incident_datetime + 5, -1) x(dt, delta)), 
rolling as (
    select *, incidents_in_range = sum(delta) 
     over (partition by device_id order by dt) 
    from incidents) 

select t.* from #temp t join rolling r 
    on r.device_id=t.device_id 
    and t.incident_datetime between r.incident_datetime - 5 and r.incident_datetime 
where r.incidents_in_range >= 3 

..basically найти точки, в которых «3 инциденты за 5 дней ", а затем присоединиться к ним, чтобы включить инциденты в течение 5 дней.

+0

Спасибо, что указали на проблему device_id, я не уделял достаточно внимания, когда делал даты. Я отредактировал ответ, чтобы удалить их. Когда я запускаю ваш запрос, я получаю некоторые нечетные результаты, хотя - device_id 2, event_id 1004 появляется (это единственная запись device_id 2), а время с ошибкой - неверно. Пример: для device_id 5, инцидент_ид 1005 он отображается как 2016-02-15, когда он должен быть 2016-02-12 (похоже, из него используется дата-время из инструкции UNION ALL) – Jericho

+0

@Jericho добавил исправить инцидент_дату, исходящий из другая сторона союза. – gordy

Смежные вопросы