lead() 是窗口函数,仅按排序取下一行数据而非预测;需严格按业务时序(如 paid_at)排序,配合 lag() 分析行为变化,并妥善处理 null 和末次记录。

LEAD() 本身不预测,只取下一行数据
LEAD() 是窗口函数,不是机器学习模型,它不会“预测”行为,只能按指定排序取出用户下一条记录的字段值。想用它模拟“下一次购买”,前提是数据已按时间排序,且你关心的是结构化的时间序列模式(比如用户购买间隔、品类切换)。
常见错误是直接写 LEAD(purchase_date) 就以为得到了预测结果——其实只是把真实发生的下一次购买时间搬了过来。真正有价值的,是结合这个值做衍生计算:
- 用
LEAD(purchase_date) OVER (PARTITION BY user_id ORDER BY purchase_date)获取每个用户下次购买时间 - 再算出
LEAD(purchase_date) - purchase_date得到实际购买间隔(单位取决于数据库,PostgreSQL 用AGE(),MySQL 用TIMESTAMPDIFF(day, ...)) - 如果下一行是不同品类,
LEAD(category)能帮你发现品类跳转规律(比如买手机后常买配件)
ORDER BY 必须严格对应业务时序
LEAD() 的结果完全依赖 ORDER BY 子句。用户可能有多个同天订单,或存在支付成功但发货延迟的记录。若仅按 purchase_date 排序,会把同天多笔订单的先后顺序搞错,导致 LEAD() 取到错误的“下一次”。
实操建议:
- 优先使用带毫秒/微秒精度的字段,如
created_at或paid_at,而不是仅日期的purchase_date - 必要时加二级排序:例如
ORDER BY paid_at, order_id,避免并行下单导致顺序不确定 - 检查是否存在 NULL 的时间字段——这些记录会被排在最前或最后,破坏时序逻辑
空值处理不当会导致分析偏差
最后一个购买记录的 LEAD() 值一定是 NULL(后面没数据了)。如果直接用 WHERE lead_purchase_date IS NOT NULL 过滤,会丢掉所有末次行为样本;但如果保留 NULL 并参与统计(比如算平均间隔),又会把 NULL 当 0 处理,拉低结果。
更稳妥的做法:
- 用
LEAD(..., 1, '9999-12-31') OVER (...)指定默认值(注意类型匹配) - 或者显式标记是否为末次行为:
CASE WHEN LEAD(user_id) OVER (...) IS NULL THEN 1 ELSE 0 END AS is_last_purchase - 计算间隔时,用
COALESCE(LEAD(paid_at) - paid_at, INTERVAL '365 days')(PostgreSQL)给缺失值设合理兜底值
和 LAG() 配合才能看出行为变化趋势
单看“下一次”容易片面。比如用户两次买奶粉间隔 30 天,第三次突然隔了 90 天——仅靠 LEAD() 看不出这是异常还是自然衰退。必须结合 LAG() 构造前后差值:
SELECT user_id, paid_at, LEAD(paid_at) OVER w AS next_paid_at, LAG(paid_at) OVER w AS prev_paid_at, LEAD(paid_at) OVER w - paid_at AS gap_to_next, paid_at - LAG(paid_at) OVER w AS gap_from_prev FROM orders WINDOW w AS (PARTITION BY user_id ORDER BY paid_at);
这样能识别出“gap_to_next 显著大于 gap_from_prev”的用户,才是真正值得运营介入的潜在流失信号。
真正难的不是写对 LEAD(),而是确认你的“下一次”定义是否贴合业务——是按支付时间?签收时间?还是加入购物车时间?选错源头,后面全错。











