lead和lag是窗口函数,分别取当前行之后或之前指定偏移量的值,需配合over(order by ...)和可选partition by使用,参数为列名、偏移量(默认1)和越界默认值(默认null)。

LEAD 和 LAG 函数的基本用法和参数含义
LEAD 和 LAG 是窗口函数,用来取当前行“之后”或“之前”的某一行的值,不需要自连接或子查询。核心区别在于:LAG 向上(前)取值,LEAD 向下(后)取值。
它们都接受三个参数:column_name(要取的列)、offset(偏移行数,默认为 1)、default_value(越界时返回值,默认为 NULL)。例如 LAG(sales, 2, 0) 表示取前两行的 sales 值,如果不存在就填 0。
- 必须配合
OVER()子句使用,且通常需要ORDER BY明确排序逻辑;没排序会导致结果不可预测 -
offset必须是常量(不能是列名或表达式),比如不能写LAG(amount, days_delay) - 分区(
PARTITION BY)很关键——跨用户、跨日期等场景必须加,否则会把不同组的数据混在一起计算
常见错误:NULL 大量出现或数据错位
最常遇到的现象是:第一行 LAG 全是 NULL,最后一行 LEAD 全是 NULL,或者相邻记录的差值明显不对。这不是函数 bug,而是排序或分区没对齐。
- 检查
OVER(ORDER BY ...)的字段是否真正唯一且符合业务顺序——比如按order_date排序,但同一天有多条记录,就会导致“谁前谁后”不确定 - 如果数据按用户分组变化,漏掉
PARTITION BY user_id,LAG就可能把张三的最后一单和李四的第一单连起来 - 时间字段含毫秒但显示被截断,肉眼看着有序,实际排序有微小差异,建议用
ORDER BY created_at, id加二级排序保序
实用场景:计算环比、识别连续状态、补缺失值
这些不是炫技,而是真实高频需求。关键是把业务逻辑映射到偏移和默认值上。
- 环比增长:用
LAG(revenue)拿上期值,再算(revenue - LAG(revenue)) / LAG(revenue);注意除零,可加NULLIF(LAG(revenue), 0) - 判断是否连续登录:用
LAG(login_date)得到前一天日期,再比对login_date = LAG(login_date) + INTERVAL '1 day' - 用
LAG(status) = status找出状态未变的连续段;配合ROW_NUMBER()可进一步编号分组 - 补缺失的 price:用
COALESCE(price, LAG(price) IGNORE NULLS)(PostgreSQL/Oracle 支持IGNORE NULLS;MySQL 8.0+ 也支持;旧版需用子查询模拟)
MySQL 8.0+ 与 PostgreSQL 的细微差异
语法主体一致,但几个细节不注意就会报错或行为不同。
- MySQL 8.0+ 支持
IGNORE NULLS,但 SQLite 和早期 MySQL 不支持;PostgreSQL 从 13 开始支持 - SQL Server 的
LAG/LEAD不支持IGNORE NULLS,且默认值参数位置和 PostgreSQL 相同,但某些旧版本对 default 参数解析更严格 - 如果在 WHERE 中引用
LAG列(如WHERE LAG(x) > 10),会报错:窗口函数不能直接用于 WHERE;必须套一层子查询或 CTE - 性能上,只要
ORDER BY字段有索引,大多数引擎能高效执行;但PARTITION BY + ORDER BY组合没索引时,可能触发临时表和文件排序
真正麻烦的不是函数本身,而是排序键的设计——它决定了“前后”是谁。业务逻辑变了,ORDER BY 往往得跟着重审,而不是只改 offset。











