You need to enable JavaScript to run this app. PostgreSQL实现同用户同类型连续事件的10分钟间隔去重统计
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL实现同用户同类型连续事件的10分钟间隔去重统计

PostgreSQL 实现自定义滚动事件窗口统计的方案

核心逻辑说明

你需要的统计规则本质是基于已确认有效事件触发的滚动10分钟窗口:

  • 按 UserID + EventName 做分组,组内所有事件按时间升序排列
  • 每识别到一个有效事件,立即从该事件时间开始生成一个10分钟的拦截窗口
  • 拦截窗口内的同组事件全部丢弃,不参与统计
  • 当后续事件时间超过当前拦截窗口的截止时间,将该事件标记为新的有效事件,重新生成10分钟拦截窗口

这个逻辑和固定时间窗口(比如自然日、整点小时切分的窗口)有本质区别:窗口的起点完全由之前的有效事件决定,不是按固定时间刻度切分。

可直接复现的实现步骤

1. 准备测试数据

先创建临时表插入对应样例数据,方便验证结果:

CREATE TEMP TABLE user_events (
    EventID INT,
    UserID INT,
    EventName VARCHAR(50),
    Timestamp TIMESTAMP
);

INSERT INTO user_events VALUES
(1,1,'search_event','2020-01-20 09:42:52'),
(2,1,'search_event','2020-01-20 09:42:58'),
(3,2,'search_event','2020-01-20 09:43:27'),
(4,1,'checkout_event','2020-01-20 09:43:49'),
(5,2,'checkout_event','2020-01-20 09:43:54'),
(6,2,'search_event','2020-01-20 09:44:12'),
(7,1,'search_event','2020-01-20 09:54:21'),
(8,1,'search_event','2020-01-20 12:45:10'),
(9,1,'search_event','2020-01-20 12:45:32'),
(10,2,'booking_event','2020-01-20 12:46:52');

2. 核心统计SQL

采用PostgreSQL支持的递归CTE实现,逻辑和人手动判断的步骤完全一致,对SQL初学者友好:

WITH RECURSIVE event_valid AS (
    -- 递归锚点:取每个用户、每类事件的最早一条作为初始有效事件
    SELECT
        EventID,
        UserID,
        EventName,
        Timestamp,
        -- 计算当前有效事件对应的10分钟拦截窗口截止时间
        Timestamp + INTERVAL '10 minute' AS window_end
    FROM (
        SELECT *,
               ROW_NUMBER() OVER (
                   PARTITION BY UserID, EventName
                   ORDER BY Timestamp ASC
               ) AS row_num
        FROM user_events
    ) sorted_event
    WHERE row_num = 1

    UNION ALL

    -- 递归遍历:顺着时间线找下一个符合窗口要求的有效事件
    SELECT
        next_event.EventID,
        next_event.UserID,
        next_event.EventName,
        next_event.Timestamp,
        next_event.Timestamp + INTERVAL '10 minute' AS window_end
    FROM event_valid curr_valid
    JOIN user_events next_event
      ON curr_valid.UserID = next_event.UserID
     AND curr_valid.EventName = next_event.EventName
     -- 只找同分组下时间更晚的事件
     AND next_event.Timestamp > curr_valid.Timestamp
     -- 仅保留超过当前拦截窗口的事件,作为新的有效事件
     AND next_event.Timestamp >= curr_valid.window_end
)
-- 输出最终有效事件
SELECT EventID, UserID, EventName, Timestamp
FROM event_valid
ORDER BY EventID;

运行结果验证

上面的SQL执行后,输出的有效事件完全匹配预期:

EventIDUserIDEventNameTimestamp说明
11search_event2020-01-20 09:42:52有效
32search_event2020-01-20 09:43:27有效
41checkout_event2020-01-20 09:43:49有效
52checkout_event2020-01-20 09:43:54有效
71search_event2020-01-20 09:54:21有效(超过Event1的10分钟窗口)
81search_event2020-01-20 12:45:10有效(超过Event7的10分钟窗口)
102booking_event2020-01-20 12:46:52有效

被自动过滤的无效事件完全符合预期:

  • EventID 2:落在Event1的10分钟拦截窗口内
  • EventID 6:落在Event3的10分钟拦截窗口内
  • EventID 9:落在Event8的10分钟拦截窗口内

新手常见避坑提示

不要用LAG()窗口函数直接取上一条事件的时间做差值判断,这种写法存在逻辑漏洞:

  • LAG()取的是物理顺序上的紧邻上一条事件,哪怕那条事件是被前面的窗口过滤掉的无效事件
  • 比如连续3个同组事件:09:40(有效)、09:45(无效,在09:40的窗口内)、09:52(和09:40间隔12分钟,本该有效),用LAG判断时会拿09:52和紧邻的09:45做差,仅间隔7分钟,就会把本该有效的事件误判为无效。
  • 递归CTE的判断逻辑永远和上一个有效事件的时间做对比,不存在这类逻辑错误。

内容的提问来源于stack exchange,提问作者Ashmeet Lamba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 02:36:07