LEAD和LAG是SQL窗口函数,分别获取当前行后/前行指定偏移量的值,必须配合OVER(ORDER BY...)使用,支持PARTITION BY分组、offset偏移量和default默认值,常用于环比计算等场景。

LEAD 和 LAG 函数的基本用法与参数含义
LEAD 和 LAG 是标准 SQL 窗口函数,用于访问当前行的“后一行”或“前一行”的值,无需自连接或子查询。它们默认只看相邻行(offset = 1),但可指定偏移量和默认值。
常见错误是忽略 ORDER BY 子句——窗口函数必须在明确排序的窗口内才有意义,否则结果不可预测。
-
LAG(column, offset, default):取当前行向上第offset行的column值;若越界,返回default(可省略,此时为NULL) -
LEAD(column, offset, default):取当前行向下第offset行的column值 - 必须配合
OVER (ORDER BY ...)使用,且ORDER BY字段应具备唯一性或足够区分时序(如加id防并列)
计算环比增长率:分步构造公式避免除零和 NULL
环比 = (本期值 − 上期值) / 上期值 × 100%,核心难点在于上期值由 LAG 提供,但直接除可能导致 division by zero 或 NULL 传播。
正确做法是把 LAG 结果单独作为一列先算出,再参与运算,并用 CASE 处理边界:
SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
CASE
WHEN LAG(revenue) OVER (ORDER BY month) IS NOT NULL
AND LAG(revenue) OVER (ORDER BY month) != 0
THEN ROUND((revenue - LAG(revenue) OVER (ORDER BY month)) * 100.0 / LAG(revenue) OVER (ORDER BY month), 2)
ELSE NULL
END AS mom_growth_pct
FROM sales;- 不要重复写四次
LAG(revenue) OVER (ORDER BY month),实际中建议用子查询或 CTE 提前计算prev_revenue列,提升可读性和执行效率 - 用
* 100.0而非* 100,避免整数除法截断(尤其在 PostgreSQL、SQL Server 中) - 首行的
prev_revenue必为NULL,所以环比必然为NULL——这是合理行为,不是 bug
多维度分组下使用 LAG:PARTITION BY 的必要性
如果数据按地区、产品线等分组计算各自环比(比如“华东区每月 vs 上月”,而非全量混排),必须加 PARTITION BY,否则跨组取值会错乱。
典型错误是只写 ORDER BY month 却漏掉 PARTITION BY region,导致北京的 2 月去拉上海的 1 月数据。
- 正确结构:
LAG(revenue) OVER (PARTITION BY region ORDER BY month) -
PARTITION BY在ORDER BY之前生效:先分组,再组内排序,最后取组内上一行 - 若分组字段有
NULL,注意不同数据库对PARTITION BY NULL的处理差异(如 MySQL 8.0 会把所有NULL归为同一组) - 复合分区如
PARTITION BY region, product_category很常见,但需确保ORDER BY字段在该组合下能线性排序
性能与兼容性注意事项
LEAD/LAG 在主流数据库(PostgreSQL、SQL Server、Oracle、MySQL 8.0+、BigQuery、Trino)都支持,但 SQLite 和旧版 MySQL 不支持——遇到 “function does not exist” 错误时先查版本。
- 窗口函数本身不强制要求索引,但
ORDER BY字段若有索引(如(region, month)联合索引),能显著加速排序阶段 - 大数据量下慎用
OFFSET > 1(如LAG(x, 12)算同比),虽语法合法,但某些引擎优化不佳 - 在 Hive/Spark SQL 中,
LAG对NULL输入的处理更严格,建议显式用COALESCE(LAG(...), 0)替代默认NULL,避免下游空指针
真正容易被忽略的是:当业务要求“上月”但数据中存在月份缺失(比如 2024-01、2024-03 连续,缺 02),LAG 取到的是 01 月而非逻辑上的“上月”。这时不能只靠窗口函数,得先补全时间维度。

















