LAG()需显式ORDER BY确保逻辑顺序,推荐拼主键防重复;首行差值应依业务定NULL或0,避免COALESCE掩盖语义;单趟扫描通常快于自连接,但无索引排序时性能相近;MySQL强制ORDER BY,PostgreSQL允许但不推荐。

LAG() 怎么写才能正确拿到上一行的值
直接用 LAG(column_name) 就能取上一行,但默认行为是按当前查询的 ORDER BY 排序后取前一行——没写 ORDER BY 会报错或结果不可靠。窗口函数不认物理行序,只认逻辑排序顺序。
常见错误是漏掉 ORDER BY,或者排序字段有重复值又没加唯一性兜底(比如时间戳相同、ID缺失),导致 LAG() 拿到的“上一行”实际是任意一条同序记录。
- 必须显式写
ORDER BY,例如ORDER BY created_at, id - 如果排序字段可能重复,建议拼上主键(如
id)确保顺序唯一 -
LAG()第二个参数可设偏移量,默认为1;第三个参数是缺省值,比如LAG(amount, 1, 0)表示首行无上一行时填0
计算差值时 NULL 怎么处理最稳妥
首行没有“上一行”,LAG() 返回 NULL,直接做减法会让整行结果变 NULL。别依赖 COALESCE() 简单兜底——它掩盖了边界语义:首行差值本就无定义,填 0 或 NULL 是业务选择,不是技术补丁。
- 明确业务规则:首行是否要显示为
0?还是留空? - 若需数值型结果且允许首行为
0,用COALESCE(LAG(val) OVER (ORDER BY ts), 0) - 若想保留语义清晰性,建议让首行保持
NULL,并在应用层判断渲染(比如前端显示 “—”) - 避免写成
val - COALESCE(LAG(val), 0):这会让首行算出val - 0,看似有数,实则逻辑错误
为什么比自连接快,以及什么情况下反而更慢
LAG() 是单趟扫描,执行计划里通常只出现一次表访问;而自连接(如 JOIN ... ON t1.id = t2.id + 1)强制数据库做关联操作,数据量大时容易触发临时表或文件排序。
- 优势场景:按时间/序列连续排序的大表(如日志、传感器数据),
LAG()几乎恒定 O(n) - 风险点:如果
ORDER BY字段无索引,窗口函数仍需排序,性能和自连接接近甚至更差 - 注意分区:加
PARTITION BY group_id后,LAG()只在组内生效,这时自连接若没对应分组逻辑,结果就完全不对
MySQL 8.0+ 和 PostgreSQL 的语法差异要点
核心语法一致,但默认行为和错误容忍度不同。MySQL 对 ORDER BY 更严格(8.0+ 强制要求),PostgreSQL 允许不写但警告,且支持更灵活的帧定义(虽然 LAG() 本身不依赖 ROWS BETWEEN)。
- MySQL:必须写
ORDER BY,否则报错Window 'w' with no ORDER BY is not allowed - PostgreSQL:不写
ORDER BY不报错,但结果非确定性,文档明确标注“不推荐” - 两者都支持
LAG(val, offset, default),但 PostgreSQL 允许default为表达式(如CURRENT_DATE),MySQL 要求字面量或列名 - PostgreSQL 中若
OFFSET超出范围,返回default;MySQL 同样行为,但旧版(
边界情况永远藏在排序唯一性和首行语义里,不是函数不会用,而是没想清楚“上一行”到底指谁。










