将时间差较小的交错记录分组

Group staggered records that are separated by a small time difference

很难回答这个问题,但我正在尝试复制社交媒体或通知提要在批量最近事件时所做的事情,以便它们可以显示操作的“序列”。例如,如果这些是 "like" 条记录,则按时间倒序排列:

like_id | user_id |   like_timestamp
--------------------------------
1       | bob     | 12:30:00
2       | bob     | 12:29:00
3       | jane    | 12:27:00
4       | bob     | 12:26:00
5       | jane    | 12:24:00
6       | jane    | 12:23:00
7       | scott   | 12:22:00
8       | bob     | 12:20:00
9       | alice   | 12:19:00
10      | scott   | 12:18:00

我想对它们进行分组,以便获得用户喜欢的最后 3 个 "bursts",按用户分组(分区?)。如果 "burst" 规则是相隔小于 5 分钟的点赞属于同一个爆发,那么我们会得到:

user_id | num_likes | burst_start | burst_end
----------------------------------------------
bob     | 3         | 12:26:00    | 12:30:00
jane    | 3         | 12:23:00    | 12:27:00
scott   | 2         | 12:18:00    | 12:22:00

alice 的点赞没有被计算在内,因为它是最近第 4 批的一部分,而点赞 8 没有被添加到 bob 的计数中,因为它比点赞早 6 分钟下一个。

我已经尝试使用 postgres 的 lag 函数来跟踪突发事件,这让我可以标记开始和结束事件,但是由于喜欢的事件可以错开,所以我无法将喜欢的事件绑定到它的 "originator"(例如,将 id 4 绑定回 2)。

这样分组可行吗?如果是这样,是否可以跟踪每个突发的开始和结束时间戳?

step-by-step demo:db<>fiddle

WITH group_ids AS (   -- 1
    SELECT DISTINCT
        user_id,
        first_value(like_id) OVER (PARTITION BY user_id ORDER BY like_id) AS group_id
    FROM
        likes
    LIMIT 3
)
SELECT 
    user_id,
    COUNT(*) AS num_likes,
    burst_start,
    burst_end
FROM (
    SELECT
        user_id,
        -- 4
        first_value(like_timestamp) OVER (PARTITION BY group_id ORDER BY like_id) AS burst_end,
        first_value(like_timestamp) OVER (PARTITION BY group_id ORDER BY like_id DESC) AS burst_start
    FROM (
        SELECT
            l.*, gi.group_id,
            -- 2
            lag(like_timestamp) OVER (PARTITION BY group_id ORDER BY like_id) - like_timestamp AS diff
        FROM
            likes l 
        JOIN
            group_ids gi ON l.user_id = gi.user_id
    ) s
    WHERE diff IS NULL OR diff <= '00:05:00'  -- 3
) s
GROUP BY user_id, burst_start, burst_end  -- 5
  1. CTE 用于为每个 user_id 创建一个有序的组 ID。所以第一个用户(这里是最近的用户)获得最低的 group_id(即 bob)。第二个用户第二高(jane)等等。这用于能够在一个分区内与某个用户的所有喜欢一起工作。此步骤是必要的,因为您不能简单地按 user_id 排序,这会使 alice 到达顶部。 LIMIT 3 将整个查询限制为前三个用户。
  2. 加入计算用户的 group_id 后,使用 lag() window function 计算时差,这样您就可以得到之前的值。所以它可以用来轻松计算当前时间戳与前一个时间戳之间的差异。这仅发生在用户组中。
  3. 之后可以通过计算diff
  4. 去除距离太远(距离上一个超过5分钟)的点赞
  5. 然后可以用first_value()window函数计算出最高和最低时间戳(升序和降序)。这些标记您的 burst_startburst_end
  6. 最后你可以对所有用户进行分组并统计他们的记录。