lead和lag默认越界返回null而非报错;需显式指定第三个参数为默认值,且类型须兼容;必须配合正确order by和partition by,否则结果不可预测;性能依赖order by字段的索引。

LEAD 和 LAG 函数到底返回 NULL?
默认情况下,LEAD 和 LAG 在越界时(比如第一行调用 LAG,或最后一行调用 LEAD)返回 NULL,不是报错,也不是跳过——这是设计行为,不是 bug。
常见错误现象:查出一堆 NULL,以为数据没排好序,其实只是没设默认值。
- 必须显式指定第三个参数作为越界默认值,例如
LAG(sales, 1, 0)表示“取前一行,取不到就填 0” - 不写默认值时,
LAG(col)等价于LAG(col, 1, NULL),别指望数据库自动猜你想填什么 - 默认值类型需和目标列兼容,
LAG(created_at, 1, '1970-01-01')比LAG(created_at, 1, 0)更安全
ORDER BY 忘了加,结果完全乱序
LEAD 和 LAG 的“前后”完全依赖窗口定义里的 ORDER BY,没有它,行为不可预测——有些数据库(如 PostgreSQL)会直接报错,MySQL 8.0+ 则可能返回随机顺序的“前一行”。
使用场景:计算环比、对比相邻时间点、标记状态变化起点。
- 窗口子句里
ORDER BY必须明确且稳定,避免用ORDER BY RAND()或含重复值却无二级排序的字段 - 如果按
user_id分组比较,记得写PARTITION BY user_id ORDER BY event_time,漏掉PARTITION BY就变成全表拉通排序 - 时间字段存在微秒级重复时,建议追加主键(如
ORDER BY event_time, id)保证排序唯一性
性能差得离谱?可能是没建对索引
窗口函数本身不慢,但底层要按 ORDER BY 字段反复扫描排序——如果这个字段没索引,大表上很容易触发 filesort 或临时磁盘表。
性能影响:千万级订单表按 created_at 做 LAG,没索引时查询从 200ms 拉长到 8s+。
- 确保
OVER (ORDER BY ...)中最左字段有单列或联合索引,例如INDEX (status, created_at)对OVER (PARTITION BY status ORDER BY created_at)有效 - MySQL 8.0.20+ 支持在窗口函数中下推索引,但仅限于
ORDER BY字段为索引最左前缀时 - PostgreSQL 中,
EXPLAIN显示WindowAgg节点旁若带Sort,且没走Index Scan,基本可判定缺索引
跨分区取值失败:PARTITION BY 写错位置
把 PARTITION BY 错写进 WHERE 或 GROUP BY,或者漏写在 OVER 里,会导致逻辑错位——比如想算每个用户的上一笔订单金额,结果算成了全站倒数第二笔。
典型错误:写成 SELECT LAG(amount) OVER (ORDER BY created_at) FROM orders GROUP BY user_id,这根本不是按用户分组,GROUP BY 和窗口函数互不感知。
-
PARTITION BY只能出现在OVER子句内,格式固定为OVER (PARTITION BY col1 ORDER BY col2) - 多级分组时(如先按地区再按用户),
PARTITION BY region, user_id是合法的,但要注意业务语义是否真需要两级隔离 - 某些旧版 Hive 或 Spark SQL 不支持
PARTITION BY+ORDER BY同时存在,需确认执行引擎版本
最容易被忽略的是排序键和分组键的粒度匹配问题:如果 PARTITION BY product_id 但 ORDER BY sale_date,而同一产品有多天销售记录,没问题;但如果 ORDER BY sale_date DESC 且当天有多个订单,又没加唯一二级排序,LAG 返回哪条就不确定了。










