LAG必须带ORDER BY,否则报错或返回随机值;排序需确定且防重复;首行返回NULL是正常行为;不可在WHERE中过滤LAG结果;偏移量和默认值应按业务设定。

能直接拿到,但必须写对 OVER 子句,否则返回 NULL 或报错——这不是函数问题,是顺序没定义清楚。
LAG 必须带 ORDER BY,否则结果不可控
SQL 没有“天然行序”,LAG() 不是从磁盘读取顺序取上一行,而是严格按 ORDER BY 定义的逻辑顺序找“前一行”。漏掉 ORDER BY,比如写成 LAG(amount) OVER (),在 PostgreSQL/SQL Server 会报错;MySQL 8.0 可能不报错但返回随机值;Oracle 则直接拒绝执行。
- 正确写法一定是
LAG(amount) OVER (ORDER BY created_at)或LAG(amount) OVER (PARTITION BY user_id ORDER BY created_at) - 排序字段要确定:避免只用
id却存在重复,建议组合created_at, id防止非确定性排序 - 方向别搞反:如果“上一行”指时间更早的记录,就得用
ORDER BY created_at ASC;若写成DESC,LAG()反而取到未来时间点
首行返回 NULL 是正常行为,不是 bug
第一行前面没有数据,LAG() 返回 NULL 是设计使然。常见错误是直接做计算,比如 amount - LAG(amount),导致整列变 NULL。
- 用
COALESCE(LAG(amount), amount)把首行“上一行”补成本身,差值为 0 - 更推荐显式判断:
CASE WHEN LAG(amount) IS NULL THEN 0 ELSE amount - LAG(amount) END - 别想在
WHERE里过滤LAG()结果——窗口函数不能出现在WHERE,只能放SELECT或HAVING(配合聚合时)
偏移量大于 1 和默认值怎么设才靠谱
LAG(col, 2) 表示取当前行向上数第 2 行的值,不是“前两行拼一起”。它仍只返回一个标量,越界就补 NULL(或你指定的默认值)。
- 想同时取前 1 行和前 2 行,得写两个调用:
LAG(col, 1) AS prev1, LAG(col, 2) AS prev2 - 默认值建议按业务设:数值类常用
0,日期类可用'1970-01-01',字符串类慎用空字符串(可能和真实空值混淆) - 偏移量设成 1000 不影响性能——窗口排序已完成,只是查内存中已排好序的数组下标
真正容易被忽略的是:分区边界和排序稳定性。比如按 PARTITION BY region ORDER BY sale_date,若某地区有多条同一天记录且未加二级排序,LAG() 在不同执行中可能指向不同行——这不是数据库 bug,是你没给足排序依据。

















