lag()和lead()返回null的根本原因是未指定order by,导致窗口函数无法确定行序;必须在over()中明确order by,且建议对排序字段建索引以避免性能问题。

MySQL 中 LAG() 和 LEAD() 为什么返回 NULL?
根本原因不是函数写错了,而是没指定 ORDER BY —— 窗口函数依赖明确的行序,而 MySQL 不保证无序查询的物理顺序。哪怕表有主键,不写 ORDER BY 就可能让 LAG() 拿到任意上一行,甚至 NULL。
- 必须在
OVER()里写ORDER BY,例如OVER (ORDER BY created_at) - 如果排序字段有重复值(比如多个记录同秒级时间),建议加二级排序,如
ORDER BY created_at, id,避免结果不稳定 -
LAG(col, 1)默认取前 1 行,但第一行没有“前一行”,所以返回 NULL;可设默认值:LAG(col, 1, 0)→ 缺失时填 0
计算相邻两行差值的典型写法(含日期/数值场景)
差值计算本质是把当前行和偏移后的某行拉到同一行做减法,LAG() 最常用,LEAD() 适合“预测下期变化”类需求。
- 同比昨日销售额:
sales - LAG(sales, 1) OVER (ORDER BY date) - 计算每笔订单距上单间隔小时数:
TIMESTAMPDIFF(HOUR, LAG(created_at) OVER (ORDER BY created_at), created_at) - 想看“下一笔订单金额比当前高多少”?用
LEAD(amount) - amount,注意LEAD()最后一行也会是 NULL
性能陷阱:ORDER BY 字段没索引会很慢
窗口函数执行前 MySQL 要先按 ORDER BY 排序整块数据,如果排序字段没索引,尤其是大表,可能触发临时文件排序,IO 暴涨。
- 检查执行计划:
EXPLAIN SELECT ... LAG() OVER (ORDER BY unindexed_col),若出现Using filesort就危险了 - 优先对
ORDER BY字段建索引,比如CREATE INDEX idx_order_time ON orders(created_at) - 别在子查询里套窗口函数再过滤——MySQL 8.0.22+ 支持下推优化,但旧版本会先算完全部再 WHERE,浪费资源
兼容性注意:5.7 及更早版本不支持窗口函数
报错 ERROR 1064 (42000): You have an error in your SQL syntax 很可能是因为 MySQL 版本太低。窗口函数是 8.0.2 引入的,不可降级使用。
- 确认版本:
SELECT VERSION();,低于8.0.2就得换思路 - 5.7 替代方案只能用自连接或变量(
@prev),但变量方式在复杂查询、并行执行下结果不可靠,不推荐线上用 - 如果无法升级,考虑在应用层做差值计算,把有序数据拉出来用代码处理
实际用的时候,最常漏掉的是 ORDER BY 和默认值设置,尤其当业务要求“首行不能为 NULL”时,LAG(col, 1, 0) 这个第三参数很容易被忽略。










