如何根据另一个事件的时间戳按顺序找到最近的事件

Posted

技术标签:

【中文标题】如何根据另一个事件的时间戳按顺序找到最近的事件【英文标题】:How do I find the most recent event in sequence based on the timestamp of another event 【发布时间】:2020-09-09 19:57:31 【问题描述】:

我有一个应用事件数据表。每行都有一个 user_id、一个时间戳、一个 page_id、一个 event_name 和其他字段。我现在关心的事件是 page_open 和 button_click 事件,但还有 10 种其他事件类型可能发生在这两者之间,比如滚动。用户可能会多次打开同一个页面,但只是经常单击页面上的一个按钮,如下例所示。

user_id  timestamp   page_id  event_name
-------  ----------  -------  --------------
     71  12:00:34        307  page_open
     88  13:01:44        307  page_open
     71  13:02:09        307  page_open
     71  13:02:11        307  scroll
     71  13:04:41        307  page_open
     71  13:04:42        307  scroll
     71  13:04:45        307  button_click_a
     71  13:08:30        307  page_open
     88  13:09:01        307  button_click_b

对于每个用户的每个 page_open 事件,我想要一个额外的列来告诉我最终是否单击了按钮。我没有要使用的页面“会话”,因此我必须查找在 button_click 时间戳之前发生的最大 page_open 时间戳。换句话说,我想把上面的表格变成下面的表格

user_id  timestamp   page_id  event_name  button_event
-------  ----------  -------  ----------  --------------
     71  12:00:34        307  page_open   NULL
     88  13:01:44        307  page_open   button_click_b
     71  13:02:09        307  page_open   NULL
     71  13:04:41        307  page_open   button_click_a
     71  13:08:30        307  page_open   NULL

我尝试将 page_open 和 button_click 事件分成两个表,并在 user_id 和 page_id 上执行LEFT JOIN,如下所示,但当然这不起作用,因为它将按钮点击与具有该 page_id 的所有 page_opens 匹配.我只想将按钮单击与其对应的 page_open 事件相匹配。

SELECT
    a.user_id,
    a.timestamp,
    a.page_id,
    a.event_name,
    b.event_name AS button_event
FROM
    (SELECT * FROM events WHERE event_name = 'page_open') a
LEFT JOIN
    (SELECT * FROM events WHERE event_name = 'button_click_a' OR event_name = 'button_click_b') b
ON
    a.user_id = b.user_id AND
    a.page_id = b.page_id
;

我不熟悉使用这样的事件数据。你能提供的任何帮助都会很棒。解决这个问题的正确方法是什么?

【问题讨论】:

请标记您正在使用的数据库。 (有版本也不错) 【参考方案1】:

这是一个孤岛问题。您需要定义以“页面打开”事件开头的相邻记录组;我会推荐一个窗口计数:

select *
from (
    select 
        t.*,
        max(case when event_name <> 'page_open' then event_name end) 
            over(partition by page_id, user_id, grp) button_event
    from (
        select 
            t.*,
            sum(case when event_name = 'page_open' then 1 else 0 end) 
                over(partition by page_id, user_id order by timestamp) grp
        from mytable t
        where event_name = 'page_open' or event_name like 'button_click_%'
    ) t
) t
where event_name = 'page_open'

你没有告诉你正在运行哪个数据库。这使用标准窗口函数语法,并且应该适用于所有支持窗口函数的数据库。

Demo on DB Fiddle

用户 ID |时间戳 | page_id |事件名称 | grp |按钮事件 ------: | :-------- | ------: | :--------- | --: | :------------- 71 | 12:00:34 | 307 |页面打开 | 1 | 88 | 13:01:44 | 307 |页面打开 | 1 | button_click_b 71 | 13:02:09 | 307 |页面打开 | 2 | 71 | 13:04:41 | 307 |页面打开 | 3 | button_click_a 71 | 13:08:30 | 307 |页面打开 | 4 |

【讨论】:

刚刚为 Hive/HQL 添加了一个标签 @sbruce:Hive 支持窗口函数,所以应该可以。 我喜欢这个答案,因为它帮助我了解了如何创建 page_num,我也可以使用它来创建 page_session_id。谢谢! 为什么在中间查询中使用max?【参考方案2】:

查找在 button_click 时间戳。

改写为查看下一行是否是按钮点击。 您没有标记 DBMS,但大多数系统支持 Lag/LEAD:

with cte as 
 (
   select
      user_id,
      timestamp,
      page_id,
      event_name,
      -- find the next non-'page_open' event
      lead(case when event_name <> 'page_open'
               then event_name
          end)
      over (partition by user_id
            order by timestamp) AS button_event
   from mytable
   where event_name in ('page_open','button_click_a','button_click_b')
 )
select *
from cte 
where event_name = 'page_open' -- remove click rows

见fiddle

【讨论】:

刚刚为 Hive/HQL 添加了一个标签 我喜欢这个答案,因为它是浓缩的。谢谢!

以上是关于如何根据另一个事件的时间戳按顺序找到最近的事件的主要内容,如果未能解决你的问题,请参考以下文章

如何根据距离找到最近的代理?

如何根据另一个组件的事件处理程序更新一个组件?

如何删除列表中重复的构造对象,同时保留顺序并在飞镖中返回列表?

查找个人最近发生的事件

BigQuery UDF 在另一个表中查找最大值

js之事件冒泡和事件捕获及其阻止详细介绍