【问题标题】:tsql check if event has occurred within a certain frequencytsql 检查事件是否在一定频率内发生
【发布时间】:2018-01-19 15:29:39
【问题描述】:

我有一个发生的失败事件表。例如,我需要找出是否在两分钟内记录了五次失败。

Id  Created                  EventType  Description
1   2015-11-16 15:10:16      1         test
2   2015-11-16 15:12:17      1         test
3   2015-11-16 15:12:18      1         test
4   2015-11-16 15:12:19      1         test
5   2015-11-16 15:12:20      1         test
6   2015-11-16 15:13:16      1         test
7   2015-11-16 16:15:43      1         test

所以在本例中,将选择事件 2-6。然后我需要选择时段的开始和结束时间。

甚至不知道从哪里开始,因为它显然是比较两行。我考虑过使用游标,但不确定是否可以使用任何巧妙的分组。

【问题讨论】:

  • 您有 SQL 2012 或更高版本吗?
  • 如果 7 发生在 2015-11-16 16:15:00 和其他 3 发生在 2015-11-16 16:15:01 应该返回什么结果?因为 6、7、8、9 和 10 将在 2 分钟的窗口内。那么现在应该有两个组:2-6 和 6-10?
  • 想象第二种情况。每 30 秒就有一个恒定的事件流。所以我们有 12:00:00、12:00:30、12:01:00、12:01:30、12:02:00、12:02:30、12:03:00、12:03: 30、12:04:00、12:04:30。那么这是否意味着 1-5 和 6-10?还是 1-5、2-6、3-7、4-8、5-9 和 6-10?如果您需要设置警报并且不希望每 2 分钟存储桶超过 1 个警报,那么我认为第一个选项是最好的。然而,这可能会更复杂。
  • 投票结束。您已被要求澄清。

标签: sql-server tsql


【解决方案1】:

您可以使用CROSS APPLY 来初始选择事件2-6:

SELECT t1.Id, t1.EventType, t1.Created AS StartTime, t3.EndTime
FROM mytable AS t1
CROSS APPLY (
   SELECT TOP 1 t2.Id, t2.Created 
   FROM mytable AS t2
   WHERE t1.EventType = t2.EventType AND
         t2.Id <> t1.Id AND
         ABS(DATEDIFF(ss, t1.Created, t2.Created)) <= 60 
   ORDER BY DATEDIFF(ss, t1.Created, t2.Created) DESC) AS t3(Id, EndTime)

输出:

Id  EventType   StartTime               EndTime
2   1           2015-11-16 15:12:17.000 2015-11-16 15:13:16.000
3   1           2015-11-16 15:12:18.000 2015-11-16 15:13:16.000
4   1           2015-11-16 15:12:19.000 2015-11-16 15:13:16.000
5   1           2015-11-16 15:12:20.000 2015-11-16 15:13:16.000
6   1           2015-11-16 15:13:16.000 2015-11-16 15:12:20.000

您现在可以检查记录桶是否包含 5 行或更多行,并使用 GROUP BY 获取第一条/最后一条记录:

SELECT MIN(t1.Id) AS StartID, 
       MAX(t3.Id) AS EndID, 
       t1.EventType, 
       MIN(t1.Created) AS StartTime, MAX(t3.EndTime) AS EndTime
FROM mytable AS t1
CROSS APPLY (
   SELECT TOP 1 t2.Id, t2.Created 
   FROM mytable AS t2
   WHERE t1.EventType = t2.EventType AND
         t2.Id <> t1.Id AND
         ABS(DATEDIFF(ss, t1.Created, t2.Created)) <= 120 
   ORDER BY DATEDIFF(ss, t1.Created, t2.Created) DESC) AS t3(Id, EndTime)
GROUP BY t1.EventType
HAVING COUNT(*) >= 5

输出:

StartID EndID   EventType   StartTime               EndTime
2       6       1           2015-11-16 15:12:17.000 2015-11-16 15:13:16.000

编辑:

另一种方法是使用Recursive CTE

;WITH CTE_RN AS (
   SELECT Id, EventType, Created,
          ROW_NUMBER() OVER (ORDER BY Created) AS rn
   FROM mytable
), CTE_Buckets AS (
   -- Anchor member: Get first row from table
   SELECT Id, EventType, Created, CAST(1 AS BIGINT) AS row_num,
          1 AS bucket_num, 0 AS time_diff
   FROM CTE_RN
   WHERE rn = 1

   UNION ALL

   -- Recursive member: Get next row. Reset time diff cumulative counter
   -- if time difference exceeds two minutes
   SELECT c1.Id, c1.EventType, c1.Created, c1.rn AS row_num, 
          -- All consecutive rows within the 2 minute time range fall within the same bucket
          bucket_num = CASE 
                          WHEN x.diff + time_diff > 120 THEN c2.bucket_num + 1
                          ELSE c2.bucket_num 
                       END,
          -- Calculate cummulative time diff. 
          time_diff = CASE 
                         WHEN x.diff + time_diff > 120 THEN 0
                         ELSE x.diff + time_diff
                      END
   FROM CTE_RN AS c1
   INNER JOIN CTE_Buckets AS c2 ON c1.rn = c2.row_num + 1
   CROSS APPLY (SELECT DATEDIFF(ss, c2.Created, c1.Created)) AS x(diff)
)   
SELECT MIN(Id) AS StartID, MAX(Id) AS EndID, 
       MIN(Created) AS StartTime, MAX(Created) AS EndTime
FROM CTE_Buckets
GROUP BY bucket_num   
HAVING COUNT(*) >= 5

此查询处理多个间隔组。

【讨论】:

  • 事件 ID 将始终为 1,因此必须使用行号
  • @DasDave 你的意思是EventType?我指的是同一EventType 有多个 事件的情况。你的数据是这样的吗?
  • 抱歉是事件类型,但问题是我将拥有多个组。
  • @DasDave 您使用的是哪个版本的 SQL Server?
  • Giorgos 我们在 sql server 2008 R2 上运行
【解决方案2】:

不是全部,但应该让你开始

select t1.ID, t2.ID, t1.Created, t2.Created, count(*) as ccount 
  from table t1 
  join table t2 
    on t2.ID <> t1.ID 
   and t2.Created > t1.Created  
   and DateDiff(minute, t2.Created, t1.Created) <= 2   
 group by t1.ID, t2.ID, t1.Created, t2.Created
having count(*) >= 5

如果你知道 ID 将是为了可以在 t2.ID > t1.ID 上使用

这可能不是我从记忆中做的确切的 DateDiff 语法

假设你在一个范围内有 7 个。这将报告任何(多个)5 人组。您需要非常具体地说明您想要什么。

【讨论】:

  • 我也是这么想的。但是如果 id 是直截了当的 - 可以在 t2.id = t1.id + 5 上创建连接
  • @Ingaz 但是你仍然需要 DateDiff 所以我不确定它会更快。如果您在时间范围内有超过 5 个,它会杀死。
  • 如果 id 没有失误就直接前进 - 可以在没有“分组依据”的情况下进行查询。但是超过 5 个的情况 - 会给出一些误报。
  • 有很多可能。但不确定 ID 是否正常,如果 2 分钟内出现超过 5 个,也不知道他们想要什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-21
  • 2020-06-15
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 2011-05-04
相关资源
最近更新 更多