LAG函数必须配合OVER子句并指定ORDER BY排序逻辑才能正确获取上一行值,否则结果不可控;默认取前1行,支持偏移量和默认值;首行无上一行返回NULL,需用COALESCE或CASE显式处理;MySQL 8.0前不支持,需变量模拟但风险高;排序必须确定,避免因时区、精度或分组顺序导致结果偏差。

LAG函数怎么写才能拿到上一行的值
直接用 LAG() 就行,但必须配合 OVER 子句定义排序逻辑,否则结果不可控。窗口函数不认表的物理存储顺序,只认 ORDER BY 指定的逻辑顺序。
常见错误是漏掉 ORDER BY,比如:LAG(amount) OVER () —— 这在多数数据库里会报错,或返回随机行,不是“上一行”。
-
LAG(column_name)默认取前 1 行,等价于LAG(column_name, 1) - 想取上两行?写成
LAG(column_name, 2) - 第三参数可设默认值,比如
LAG(amount, 1, 0),当没有上一行时返回0而非NULL - 排序字段必须具有确定性,避免用
id但存在重复值,优先用created_at+id复合排序
对比当前行和上一行的差值总报NULL怎么办
本质是第一行没有“上一行”,LAG() 自然返回 NULL,再做减法(如 amount - LAG(amount))就全变成 NULL。这不是 bug,是预期行为。
解决思路不是消灭 NULL,而是显式处理它:
- 用
COALESCE(LAG(amount), amount)把首行的上一行值补成自身,差值为 0 - 用
CASE WHEN LAG(amount) IS NULL THEN 0 ELSE amount - LAG(amount) END更清晰表达业务逻辑 - 别依赖
WHERE LAG(...) IS NOT NULL—— 窗口函数不能出现在WHERE,只能放SELECT或HAVING(如果带聚合)
LAG在MySQL 8.0之前根本不能用,有没有替代方案
确实不行。MySQL 5.7 及更早版本不支持窗口函数,强行用会报错 ERROR 1064: You have an error in your SQL syntax。
可行替代方式只有变量模拟,但要注意执行顺序风险:
- 必须用子查询或 CTE 先排序,再在外部用用户变量逐行计算,例如:
@prev := @curr和@curr := amount - 变量赋值顺序依赖 MySQL 的执行计划,不同版本/优化器可能出错,生产环境慎用
- 如果数据量大、有并发写入,变量状态还可能被干扰,不如升级到 MySQL 8.0+ 直接用
LAG() - PostgreSQL、SQL Server、Oracle、BigQuery 都原生支持,无需降级方案
用LAG计算环比时为什么结果和Excel对不上
大概率是排序逻辑不一致。Excel 默认按你肉眼看到的行序算“上一行”,而 SQL 的 ORDER BY 如果没写全,数据库可能按索引顺序或任意顺序返回。
特别容易忽略的点:
- 时间字段含秒级精度但显示只到日,导致同一天多条记录排序未定义 → 必须加二级排序,如
ORDER BY dt, id - 业务上“上一日”不等于“上一行”,需先按天聚合再用
LAG(),否则会拿同一日内的前一条记录作对比 - 时区没对齐:数据库用 UTC,应用层用本地时区,
dt字段值实际跨天了 -
LAG()是按窗口定义的顺序取值,和GROUP BY的分组粒度无关;先GROUP BY再LAG,别反过来
真实场景里,最常卡住人的不是语法写不对,而是没意识到“上一行”这个概念本身必须由你明确定义——它不存在于数据里,只存在于你的 ORDER BY 里。

















