lag和lead是窗口函数,用于获取当前行前/后指定偏移量的值,必须配合含order by的over子句使用;lag(column, offset, default)取前行,lead取后行,offset默认为1,default默认null,排序字段需具确定性以防错位。

LAG和LEAD函数的基本用法是什么
LAG和LEAD是窗口函数,用于访问当前行之前或之后的某一行数据,不需要自连接或子查询。它们必须配合OVER子句使用,且要求明确指定排序逻辑——没有ORDER BY会报错或结果不可靠。
常见错误现象:Window function requires an OVER clause with ORDER BY;或者看似能执行,但返回NULL过多,其实是排序字段有重复值导致窗口内顺序未定义。
-
LAG(column, offset, default):取前offset行的column值,找不到时返回default(可省略,默认为NULL) -
LEAD(column, offset, default):取后offset行的column值,同上 - 排序字段必须具有确定性,推荐用主键或带唯一约束的列,避免仅用
created_at这种可能重复的时间戳
如何处理排序字段重复导致的LAG/LEAD错位
当ORDER BY字段存在重复值(如多个订单同秒创建),数据库无法保证这些行之间的相对顺序,LAG/LEAD就可能“跳过”或“拉错”行。这不是函数bug,而是窗口定义不严谨的结果。
实操建议:
- 在
ORDER BY中追加一个唯一字段兜底,例如:ORDER BY created_at, id - 如果业务上确实允许“相同时间视为无序”,需提前用
ROW_NUMBER() OVER (ORDER BY ...)生成稳定序号,再基于该序号做LAG/LEAD - MySQL 8.0+、PostgreSQL、SQL Server 2012+、Oracle 10g+均支持,但SQLite需3.25.0+;旧版MySQL(
LAG/LEAD在实际分析中的典型场景
不是所有“前后行”需求都适合LAG/LEAD——它只适用于单表或已聚合后的结果集。一旦涉及多表关联或条件过滤,要注意WHERE和窗口函数的执行顺序:WHERE先于窗口计算,所以过滤后才计算LAG/LEAD。
几个高频用例:
- 计算环比:用
LAG(sales_amount)拿到上期值,再与当前行相减或相除 - 识别状态变化:比如用户等级从
'silver'变成'gold',可用LAG(level) != level定位变更行 - 补全缺失时间点:先用
GENERATE_SERIES(PostgreSQL)或递归CTE构造完整日期序列,LEFT JOIN业务表后再用LAG/LEAD填充最近的有效值 - 注意性能:对大表慎用,尤其当
OVER子句未命中索引时,可能触发全表排序
为什么LAG(…, 1)有时返回NULL而预期有值
最常被忽略的一点:LAG/LEAD的“行偏移”是按OVER定义的逻辑顺序计算的,不是物理存储顺序。即使数据看起来“连续”,只要排序后首行前面没数据,LAG(col, 1)就一定是NULL。
排查步骤:
- 单独运行
SELECT *, ROW_NUMBER() OVER (ORDER BY x) AS rn FROM t,确认排序后行号是否连续、首尾是否符合预期 - 检查是否有隐式类型转换干扰排序,比如把字符串型日期
'2023-10-01'和'2023-9-30'混排,会导致字典序错乱 - 若用在分组内(
PARTITION BY user_id),确保每组内至少有2行数据,否则组内首行的LAG必为NULL
真正麻烦的是那种“以为数据够,其实被PARTITION或WHERE悄悄截断了”的情况——这类问题往往要回溯整个查询结构才能定位。










