假设我有以下表格:
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。
有什么好的建议吗?
答案 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}}唯一} p>
答案 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;
它不是很快,而且不是很干净,如果有人找到更好的解决方案,请告诉我!