“会话切割”是在日志分析中按用户活跃间隔(如30分钟无操作)将时间序列行为流切分为逻辑会话的过程,核心用lag()获取前一行时间、case标记新会话起点、sum()over累计生成唯一session_id。

什么是“会话切割”在日志分析中的实际含义
日志本身没有天然的会话边界,所谓“会话切割”,本质是把按时间排序的用户行为流,按活跃间隔(如 30 分钟无操作)切分成多个逻辑会话。窗口函数本身不直接“切分”数据,它只提供行上下文能力;真正切割依赖 LAG() 或 LEAD() 获取前/后一行时间,再结合累计求和(SUM() OVER)打上会话 ID。
用 LAG + CASE + SUM(OVER) 构建会话 ID
核心思路:判断当前行与上一行的时间差是否超过阈值,是则标记为新会话起点(1),否则为延续(0),再用累计和生成唯一会话 ID。
常见错误是直接对 timestamp 做 GROUP BY 或误用 ROW_NUMBER()——这只会编号不切组。
-
LAG(timestamp) OVER (PARTITION BY user_id ORDER BY timestamp)必须显式指定PARTITION BY user_id,否则跨用户混算 - 时间差计算需统一单位:PostgreSQL 用
EXTRACT(EPOCH FROM ...),MySQL 用TIMESTAMPDIFF(SECOND, ..., ...),BigQuery 用TIMESTAMP_DIFF(..., ..., SECOND) - 累计和必须用
SUM(is_new_session) OVER (PARTITION BY user_id ORDER BY timestamp ROWS UNBOUNDED PRECEDING),漏掉ROWS UNBOUNDED PRECEDING可能导致部分数据库(如旧版 MySQL)默认为 RANGE,引发重复计数
SELECT
user_id,
timestamp,
action,
SUM(is_new_session) OVER (
PARTITION BY user_id
ORDER BY timestamp
ROWS UNBOUNDED PRECEDING
) AS session_id
FROM (
SELECT *,
CASE
WHEN timestamp - LAG(timestamp) OVER (
PARTITION BY user_id ORDER BY timestamp
) > INTERVAL '30 minutes'
THEN 1
ELSE 0
END AS is_new_session
FROM raw_logs
) t;
不同数据库对时间间隔判断的写法差异
窗口函数语法一致,但时间运算接口不兼容,硬套会报错。
- PostgreSQL:
timestamp - LAG(timestamp) OVER (...) > INTERVAL '30 minutes' - MySQL:
TIMESTAMPDIFF(SECOND, LAG(timestamp) OVER (...), timestamp) > 1800 - BigQuery:
TIMESTAMP_DIFF(timestamp, LAG(timestamp) OVER (...), SECOND) > 1800 - Trino/Presto:
date_diff('second', LAG(timestamp) OVER (...), timestamp) > 1800
注意:MySQL 8.0+ 才支持窗口函数,5.7 及以下无法执行;BigQuery 中 LAG() 对 NULL 的处理更严格,建议加 COALESCE(LAG(...), TIMESTAMP('1970-01-01')) 防空值中断累计和。
性能和边界情况必须检查的三点
会话切割在千万级日志上容易慢或出错,不是语法对就万事大吉。
- 原始表必须有
(user_id, timestamp)复合索引,否则LAG() OVER (PARTITION BY user_id ORDER BY timestamp)会全表扫描 - 存在同一秒内多条日志时,仅靠
timestamp排序可能不稳定,建议追加唯一字段如log_id:ORDER BY timestamp, log_id - 用户首次行为永远视为新会话起点,但
LAG()对首行返回 NULL,需确保CASE中把 NULL 视为超时(即WHEN ... IS NULL OR timestamp - LAG(...) > ... THEN 1)
会话切割真正难的不是写对第一行 SQL,而是确认你定义的“30 分钟”是否覆盖了所有设备时区、日志采集延迟、以及用户真实离线行为——这些没法靠窗口函数自动修正。










