【问题标题】:Find events that occured x-times during given period查找在给定时间段内发生 x 次的事件
【发布时间】:2012-04-05 12:33:09
【问题描述】:

假设我有下表:

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

其中包含对象的 ID(不唯一,它重复)和观察到此对象 ID 的时间戳。

观察 24/7 全天候运行,并插入带有当前时间戳的每个对象 ID。

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

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

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

有什么好的建议吗?

【问题讨论】:

  • 为什么要将时间戳存储为整数值?
  • 因为我对确切的时间/日期不感兴趣,但对出现之间的差异不感兴趣。使用整数进行计算比我预期的更快
  • @rkosegi,你需要纯 mysql 答案还是 PHP 混合可以?
  • 我根本没有使用 PHP,我知道如何使用附加代码,所以需要纯 SQL。
  • 尝试搜索“按时间(戳)间隔分组”,它会得到你manyyyresults:-)跨度>

标签: mysql count group-by


【解决方案1】:

这应该可行:

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_* 变量来创建每行的索引,模拟其他 rdbms 已知的 row_number() 功能)。
然后它只是检查两行是否引用相同的object_id并满足周期要求。
由于对每个出现行都执行此操作,因此如果出现次数实际上大于@max_occurences,则 object_id 将被多次返回,因此最后将其分组以使返回的object_ids 唯一

【讨论】:

    【解决方案2】:

    你可以试试

    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...

    【讨论】:

    • 我使用时间戳是因为程序的其他部分需要时间戳。我认为这不可用,因为没有“your_dt”。选择应该查看整个表并找到出现 7 或的对象 ID任何 10 分钟间隔内的更多次。想象一下“谁在 10 分钟间隔内访问我的网站 7 次或更多次”(不是持续 10 分钟)
    • 您无法仅使用一条 SQL 语句获得任何时间范围。
    【解决方案3】:

    您可以使用以下语句:

    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;
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 2012-01-02
      相关资源
      最近更新 更多