查找在给定时间段内发生x次的事件

时间:2012-04-05 12:33:09

标签: mysql count group-by

假设我有以下表格:

CREATE TABLE `occurences` (
  `object_id` int(10) NOT NULL,
  `seen_timestamp` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

包含对象的ID(不唯一,重复)以及观察此对象ID时的时间戳。

观察正在全天候运行并插入每次出现的对象ID和当前时间戳。

现在我想编写查询来选择在任何10分钟内至少看过7次的所有对象ID。

它应该像检测入侵一样起作用。

在denyhost脚本中使用类似的算法来检查无效的SSH登录。 如果在配置的时间段内找到配置的出现次数,则会阻止IP。

有什么好的建议吗?

3 个答案:

答案 0 :(得分:4)

这应该有效:

SET @num_occurences = 7; -- how many occurences should occur in the interval
SET @max_period = 10; -- your interval in seconds

SELECT offset_start.object_id FROM 
(SELECT @rownum_start := @rownum_start+1 AS idx, object_id, seen_timestamp 
 FROM occurences, (SELECT @rownum_start:=0) r ORDER BY object_id ASC, seen_timestamp ASC) offset_start
JOIN
(SELECT @rownum_end := @rownum_end + 1 AS idx, object_id, seen_timestamp 
 FROM occurences, (SELECT @rownum_end:=0) r ORDER BY object_id ASC, seen_timestamp ASC) offset_end
   ON offset_start.object_id = offset_end.object_id 
  AND offset_start.idx + @num_occurences - 1 = offset_end.idx
  AND offset_end.seen_timestamp - offset_start.seen_timestamp <= @max_period
GROUP BY offset_start.object_id;

您可以将@num_occurences@num_occurences移到代码中,并将其设置为语句的参数。根据您的客户端,您还可以在查询前移动@rownum_start@rownum_end的初始化,这可能会提高查询性能(您应该测试一下,但只是看到了解释两个版本)

以下是它的工作原理:

它选择整个表两次,并将offset_start的每一行与offset_end中的行连接,其中@num_occurences的偏移量为@rownum_*。 (这是使用@max_occurences变量来创建每行的索引,模拟其他rdbms中已知的row_number()功能。) 然后它只是检查两行是否引用相同的object_id并满足周期要求 由于这是针对每个出现的行进行的,如果出现的次数实际上大于object_id,则会多次返回object_id,因此将其分组到最后以使返回的{{1}}唯一

答案 1 :(得分:1)

你可以尝试

SELECT COUNT(seen_timestamp) AS tot FROM occurences
WHERE seen_timestamp BETWEEN
    DATE_ADD(your_dt, INTERVAL -10 MINUTES) AND your_dt
GROUP BY object_id
HAVING tot >= 7

我不明白您为int(10)使用seen_timestamp的原因:您可以使用datetime ...

答案 2 :(得分:1)

您可以使用以下语句:

SELECT oc1.object_id 
    FROM occurences oc1 
        JOIN occurences oc2 ON oc1.object_id = oc2.object_id  
            AND oc1.seen_timestamp >= (oc2.seen_timestamp -600)
            AND oc1.seen_timestamp < oc2.seen_timestamp
    GROUP BY oc1.object_id, oc1.seen_timestamp
    HAVING COUNT(oc2.object_id)>=7;

它不是很快,而且不是很干净,如果有人找到更好的解决方案,请告诉我!