SQL 查询 select 个活动会话

SQL query to select event sessions

假设我们有一个包含两列 user_idevent_time 的 table,代表应用程序中可能发生的事件。

我们想编写一个 SQL 查询来查找用户会话。会话被定义为特定事件的一系列连续事件,其中 none 事件间隔超过一定时间(类似于 Google Analytics 定义会话的方式)。

例如,如果我们有以下 table:

    user_id | time 
   ---------+------
          1 |    1
          1 |    2
          1 |    3
          1 |    4
          1 |   20
          1 |   22
          1 |   26
          1 |   28
          1 |   30
          2 |    2
          2 |    4
          2 |    6
          3 |   15
          3 |   30

我们可能会产生以下输出:

    user_id | start | end
   ---------+------+------
          1 |     1 |   4
          1 |    20 |  30
          2 |     2 |   6
          3 |    15 |  15
          3 |    30 |  30

如果您使用的是 MS SQL 服务器,那么您可以使用 CTE 和窗口函数来实现此目的:

DECLARE @my_table TABLE (userid INT NOT NULL, timeid INT NOT NULL)

INSERT INTO @my_table (userid, timeid)
VALUES
    (1, 1),
    (1, 2),
    (1, 3),
    (1, 4),
    (1, 20),
    (1, 22),
    (1, 26),
    (1, 28),
    (1, 30),
    (2, 2),
    (2, 4),
    (2, 6),
    (3, 15),
    (3, 30)

;WITH CTE_With_Previous AS
(
    SELECT
        userid,
        timeid,
        LAG(timeid) OVER (PARTITION BY userid ORDER BY timeid) AS last_time
    FROM
        @my_table
),
CTE_Range_Starts AS
(
    SELECT
        userid,
        timeid,
        LEAD(timeid) OVER (PARTITION BY userid ORDER BY timeid) AS next_group_start_time
    FROM CTE_With_Previous
    WHERE
        timeid - last_time > 5 OR last_time IS NULL
)
SELECT
    S.userid,
    S.timeid AS start_time,
    (SELECT MAX(MT.timeid) AS end_time FROM @my_table MT WHERE MT.userid = S.userid AND (MT.timeid < S.next_group_start_time OR S.next_group_start_time IS NULL))
FROM CTE_Range_Starts S

此查询的工作原理是获取每行的结果集以及前一行的 timeid(按 timeid 排序)。这使得下一个 CTE 中的数学运算更容易,它会查找 timeid 和前一个 timeid 之间的差异大于 5 的所有情况——这意味着这将是新会话的开始。随着每个开始时间,我们找到下一个会话的开始时间(LEAD 函数)。

现在我们有了每个会话的开始时间以及同一行中那个会话之后的下一个会话的开始时间,我们只需要获取这些会话的结束时间,这就是最后一个 timeid ( MAX) 在下一个会话之前。