LEAD和LAG函数必须搭配ORDER BY才能正确工作,否则返回NULL;偏移量默认为1可省略,但首参须为具体列;窗口子句不可省略ORDER BY,重复排序字段需加唯一键保障稳定性。

LEAD 和 LAG 函数怎么写才不报错
直接用 LEAD 或 LAG 却返回 NULL,不是函数错了,是没配对的 ORDER BY。这两个函数必须搭配窗口定义里的排序,否则数据库无法判断“前一行”或“后一行”是谁。
-
LEAD(col, 1)和LAG(col, 1)的第二个参数是偏移量,默认为 1,可省略;但第一个参数(列名)不能是表达式或常量,必须是具体列 - 窗口子句必须显式写
OVER (ORDER BY ...),不能只写OVER ()—— PostgreSQL 会报错,MySQL 8.0+ 和 SQL Server 会默认按无序处理,结果不可靠 - 如果排序字段有重复值(比如多个订单同时间创建),
LEAD返回的“下一行”可能随机,建议补上唯一键:ORDER BY create_time, id
想查某行前后各 N 行,为什么不能只用 LEAD/LAG 各一次
因为 LEAD 和 LAG 每次只能取一个偏移位置的数据,要拿到“前 2 行、当前行、后 2 行”,得分别调用 5 次函数,而不是靠一个参数控制范围。
- 查前后各 1 行:需要
LAG(col, 1)、col、LEAD(col, 1)三个字段 - 查前后各 2 行:就得写
LAG(col, 2)、LAG(col, 1)、col、LEAD(col, 1)、LEAD(col, 2) - 别指望
LEAD(col, 1, 2)这种写法 —— 第三个参数是 default 值,不是范围,SQL 标准不支持批量偏移
WHERE 条件里能直接用 LAG/LEAD 吗
不能。窗口函数不能出现在 WHERE 或 GROUP BY 中,因为它们在逻辑执行顺序里晚于这些子句。想筛选“前一行值等于 X”的记录,得套一层子查询或 CTE。
- 错误写法:
WHERE LAG(status) OVER (ORDER BY id) = 'pending'→ 语法错误 - 正确做法:先在子查询里算出
LAG(status)列,再在外层WHERE过滤,例如:SELECT * FROM (SELECT *, LAG(status) OVER (ORDER BY id) AS prev_status FROM orders) t WHERE prev_status = 'pending'
- 性能注意:如果表很大,又只想要某几行的前后数据,先用主键范围过滤(如
id BETWEEN ? AND ?)再开窗,比全表开窗快得多
MySQL 5.7 不支持 LAG/LEAD,有没有轻量替代方案
没有等价函数,但可以用自连接或变量模拟,前提是排序字段严格唯一且可索引。
- 用自连接:把表自联两次,用
ON t1.id = t2.id - 1拿前一行,ON t1.id = t2.id + 1拿后一行 —— 要求id是连续整数,否则会漏行 - 用用户变量(MySQL 5.7):
SELECT id, val, @prev AS prev_val, @prev := val FROM t, (SELECT @prev := NULL) r ORDER BY id
,但变量行为在复杂查询中不稳定,官方已不推荐 - 最稳妥的退路:升级到 MySQL 8.0+,或改用应用层拼接(查出目标行和附近 ID,再发两条额外查询)
实际用的时候,最容易被忽略的是排序字段的稳定性 —— 看似加了 ORDER BY created_at,但如果这个字段大量重复,LAG 返回的“前一行”每次执行都可能不一样,连测试都难复现。真要靠它做业务逻辑,一定补上唯一性保障。

















