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

Mysql 查找在给定时间段内发生x次的事件,mysql,count,group-by,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 它的功能应该类似

假设我有下表:

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

有什么好建议吗?

你可以试试

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
我不明白为什么使用int10作为时间戳:可以使用datetime…

这应该可以: 您可以将@num\u occurrences和@num\u occurrences移动到代码中,并将它们设置为语句的参数。根据您的客户机,您还可以将@rownum_start和@rownum_end的初始化移动到查询前面,这可能会提高查询性能。您应该测试这两个版本的解释

下面是它的工作原理: 它选择整个表两次,并将offset_start的每一行与offset_end中偏移量为@num_occurrences的行连接起来。这是通过使用@rownum_u*变量来创建每行的索引来完成的,模拟其他rdbms已知的行数功能。 然后它只检查这两行是否引用了相同的object_id并满足周期要求。
由于这是针对每个发生行执行的,因此如果发生次数实际大于@max\u occurrences,则将多次返回对象id,因此最终将其分组以使返回的对象id唯一

您可以使用以下语句:

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;

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

我使用时间戳是因为程序的其他部分需要时间戳。我认为这是不可用的,因为没有,所以您的_dt.Select应该查看整个表,并找到在任何10分钟间隔内出现7次或更多次的对象ID。想象一下,如果有人在10分钟间隔内访问我的站点7次或更多次,而不是最后10分钟,您不能仅使用一条SQL语句获得任何时间范围。为什么要将时间戳存储为整数值?因为我对确切的时间/日期不感兴趣,但对事件之间的差异感兴趣。使用整数进行计算的速度比I更快expect@rkosegi,您需要纯mysql答案,还是PHP混合可以?我根本不使用PHP,我知道如何使用传统代码,因此需要纯SQL。请尝试按时间戳间隔搜索组,我已经测试了你的查询,结果看起来是正确的。我希望得到这样的准确答案。谢谢。
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;