LAG()和LEAD()本身不计算间隔,需配合时间差函数(如TIMESTAMPDIFF、EXTRACT);必须用PARTITION BY user_id分组并ORDER BY order_time(建议加order_id防重复)确保同用户内有序计算,否则跨用户混序导致间隔错误。

LEAD() 和 LAG() 不是预测工具,而是按排序取相邻行的值;真正能算出“间隔时间”的,是它们配合时间差函数(如 TIMESTAMPDIFF、DATEDIFF 或 EXTRACT)之后的结果。
为什么直接写 LAG(order_time) 会得到错误的时间间隔
常见错误是只写 LAG(order_time) OVER (ORDER BY order_time),却没加 PARTITION BY user_id。这会导致 A 用户的上一笔订单,被错当成 B 用户的上一笔——因为全表按时间排,跨用户混序了。结果就是间隔计算完全失真。
必须明确两点:
-
PARTITION BY user_id是分组前提,确保每个用户独立拉自己的上一行 -
ORDER BY order_time必须带精度:如果order_time是秒级且存在重复,建议补二级排序,例如ORDER BY order_time, order_id,否则窗口行为不稳定 - MySQL 8.0+、PostgreSQL、SQL Server 都支持该语法,但老版本 MySQL(
TIMESTAMPDIFF 在 MySQL 中怎么用才不出错
MySQL 不允许直接用 order_time - LAG(order_time),会报错或返回意外整数。必须用 TIMESTAMPDIFF 显式指定单位和顺序:
TIMESTAMPDIFF(SECOND, LAG(order_time) OVER (PARTITION BY user_id ORDER BY order_time), order_time)
注意三点:
- 参数顺序是
(unit, start, end),别把前后颠倒——LAG()是起点,当前行是终点 - 单位必须小写:如
SECOND、DAY、MINUTE,大小写敏感 - 如果想算天数但忽略时分秒,先用
DATE(order_time)截断,再传入TIMESTAMPDIFF
NULL 值怎么处理才不影响统计口径
首笔订单的 LAG() 永远是 NULL,末笔订单的 LEAD() 也永远是 NULL。直接参与减法会导致整行结果为 NULL,后续 AVG()、COUNT() 都会漏数据或出错。
推荐做法不是删掉这些行,而是显式标记或兜底:
- 用
COALESCE(TIMESTAMPDIFF(...), 0)补 0——仅适用于“首次购买不算间隔”的业务场景 - 更安全的是保留
NULL,并在外层加判断:CASE WHEN LAG(order_time) OVER (...) IS NULL THEN 'first_order' ELSE 'repeat_order' END - 若需计算平均间隔,过滤条件应写在
WHERE子句:WHERE LAG(order_time) OVER (PARTITION BY user_id ORDER BY order_time) IS NOT NULL(注意:这个WHERE是对窗口计算后的结果过滤,不是在窗口内)
性能容易被忽略的关键点
当订单表超百万行,PARTITION BY user_id ORDER BY order_time 的窗口计算可能明显变慢——尤其当没有对应索引时。
必须建复合索引:
CREATE INDEX idx_user_order_time ON orders(user_id, order_time);
另外两个隐性坑:
- 如果
order_time允许为NULL,它会在排序中被排到最前或最后,破坏真实时序,建议提前WHERE order_time IS NOT NULL - 时区不一致(比如部分记录带 UTC、部分带本地时区)会让
TIMESTAMPDIFF结果偏差数小时,上线前务必统一转换










