2016-04-28 50 views
3

我有一个表跟踪为特定的Device_IDs创建的Incident_IDs的Datetime,我试图找到一种方法来跟踪日期范围内的慢性问题。慢性问题的定义是在过去5天内创建了3个或更多Incident_ID的任何Device_ID。我需要能够搜索一系列不同的日期(通常是每月)。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 

我曾尝试下面的查询,显示我的事件的上升计数和让我找到那些长期存在问题的device_id,但我很难隔离构成慢性问题的所有事件,同时排除在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

下面是内n天总得出一个正在运行的事件的方式:

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,incident_id 1004显示出来(这是唯一的device_id 2条目),而且incident_datetime不正确。例如:对于device_id 5,incident_id 1005,它显示为2016-02-15,它应该是2016-02-12(看起来像是使用UNION ALL语句的datetime) – Jericho

+0

@Jericho添加了一个修复程序incident_datetime来自所有工会的另一面。 – gordy