LEAD()需配合PARTITION BY user_id和ORDER BY visit_time使用,否则会跨用户串行;字符串时间须转为时间类型再排序;判断连续路径须结合LEAD(path)与时间间隔双重验证。

LEAD() 怎么取下一条路径才不串行
直接写 LEAD(path) 很容易把不同用户的路径混在一起,比如用户 A 的最后一条记录后面,接上用户 B 的第一条记录——因为没分组。必须加 PARTITION BY user_id,确保每个用户各自独立“往下看”。ORDER BY visit_time 也必不可少,否则时间乱序,LEAD() 拿到的就不是真正“下一次访问”的路径。
常见错误写法:LEAD(path) OVER (ORDER BY visit_time) —— 全表排,跨用户跳;正确写法:LEAD(path) OVER (PARTITION BY user_id ORDER BY visit_time)。
如果 visit_time 是字符串(如 '2024-03-01 14:22:05'),得先转成 TIMESTAMP 或 DATETIME,否则隐式排序可能出错;MySQL 可用 STR_TO_DATE(visit_time, '%Y-%m-%d %H:%i:%s'),PostgreSQL 用 TO_TIMESTAMP(visit_time, 'YYYY-MM-DD HH24:MI:SS')。
怎么判断“连续访问路径”是否真连续
连续路径 ≠ 相邻两条记录的 path 字符串一样,而是指用户在真实访问流中,从页面 A 点击跳转到页面 B —— 这个“跳转关系”必须由时间顺序和行为逻辑共同定义。所以不能只比 path 值,得结合 LEAD(path) 和时间间隔做双重验证:
- 用
DATEDIFF(LEAD(visit_time), visit_time)(MySQL)或LEAD(visit_time) - visit_time(PostgreSQL)算出两次访问间隔,过滤掉 > 30 分钟的断点(业务可调) - 排除刷新行为:若
path = LEAD(path)且间隔 - 路径需标准化:/order?id=123 和 /order/?id=123 应统一为
/order,否则会被当成两条不同路径
WHERE 里不能直接用 LEAD() 计算结果
很多人写完 SELECT *, LEAD(path) OVER (...) AS next_path,接着在 WHERE next_path = '/checkout',结果报错 column "next_path" does not exist。这是 SQL 执行顺序决定的:WHERE 在窗口函数执行前就运行了。
必须包一层子查询或 CTE:
WITH path_flow AS (
SELECT user_id, visit_time, path,
LEAD(path) OVER (PARTITION BY user_id ORDER BY visit_time) AS next_path,
LEAD(visit_time) OVER (PARTITION BY user_id ORDER BY visit_time) AS next_time
FROM visits
)
SELECT user_id, path, next_path
FROM path_flow
WHERE next_path = '/checkout'
AND next_time - visit_time < INTERVAL '30 minutes';注意:MySQL 不支持 INTERVAL 直接减时间,得用 TIMESTAMPDIFF(MINUTE, visit_time, next_time) 。
路径去重为什么必须放在 LEAD 之前
用户可能 1 秒内狂点 5 次同一链接,产生 5 条重复 path 记录。如果直接对原始表跑 LEAD(),就会出现 “A → A → A → B” 这种伪连续链,实际只是单页刷新后跳转。
正确做法是先聚合去重:
- PostgreSQL:
SELECT DISTINCT ON (user_id, DATE(visit_time), path) * - MySQL:
GROUP BY user_id, DATE(visit_time), path,再取MIN(visit_time)作为该次访问时间 - 去重后再跑
LEAD(),才能反映真实用户导航意图
漏掉这步,所有后续的“路径序列分析”都会失真——不是函数用错了,是输入数据本身就不符合“一次访问一个动作”的建模前提。

















