MySQL中基于events表计算用户会话数及时长总和的SQL查询
计算用户会话数与总会话时长的MySQL查询方案
这是用户行为分析里很典型的会话统计需求,我会一步步带你实现对应的SQL逻辑:
核心思路
要统计会话,关键是给同一用户的连续事件划分会话组:当用户的两个相邻事件间隔超过2分钟(120秒)时,就认为前一个会话结束,新会话开始。之后我们只需要按用户和会话组来统计数量与时长即可。
完整SQL代码
WITH event_with_prev_time AS ( -- 第一步:获取每个用户上一条事件的发生时间 SELECT UserId, EventTime, LAG(EventTime) OVER (PARTITION BY UserId ORDER BY EventTime) AS prev_event_time FROM events ), session_groups AS ( -- 第二步:给每个事件分配会话ID,同一会话的ID相同 SELECT UserId, EventTime, SUM( CASE -- 首次事件或与上一事件间隔>2分钟时,标记为新会话起点 WHEN prev_event_time IS NULL OR TIMESTAMPDIFF(SECOND, prev_event_time, EventTime) > 120 THEN 1 ELSE 0 END ) OVER (PARTITION BY UserId ORDER BY EventTime) AS session_id FROM event_with_prev_time ) -- 第三步:按用户统计会话数和总时长 SELECT UserId, COUNT(DISTINCT session_id) AS session_count, -- 会话数量 -- 将总秒数转换为时分秒格式(如'00:02:34') SEC_TO_TIME(SUM(TIMESTAMPDIFF(SECOND, MIN(EventTime), MAX(EventTime)))) AS total_session_duration FROM session_groups GROUP BY UserId;
代码逐段解释
event_with_prev_timeCTE
使用LAG()窗口函数,按用户分组、事件时间排序,获取当前事件的上一条事件时间。这一步是为了计算相邻事件的时间间隔。session_groupsCTE
通过SUM() OVER()累计窗口函数来生成会话ID:- 如果是用户的第一条事件(
prev_event_time IS NULL),直接标记为新会话(加1) - 如果当前事件和上一条事件的间隔超过120秒,也标记为新会话(加1)
- 否则保持当前会话ID不变(加0)
这样同一连续会话内的所有事件会拥有相同的session_id。
- 如果是用户的第一条事件(
最终统计
COUNT(DISTINCT session_id):统计每个用户的会话总数SUM(TIMESTAMPDIFF(SECOND, MIN(EventTime), MAX(EventTime))):先计算每个会话的时长(会话最后一条事件时间减第一条),再求和得到总时长,最后用SEC_TO_TIME()转换为友好的时分秒格式。
特殊情况说明
- 如果用户只有1条事件:会话数为1,会话时长为0(因为没有时间间隔),结果会显示为
00:00:00,这符合逻辑。 - 如果用户的事件间隔刚好等于2分钟:不会开启新会话,因为我们定义的是连续2分钟无事件才结束会话,等于2分钟的话属于连续活跃。
内容的提问来源于stack exchange,提问作者AJ222




