LAG是必须配合OVER子句使用的窗口函数,需显式指定ORDER BY,支持PARTITION BY分组,常用于计算环比(MoM),但不能直接用于WHERE/GROUP BY;同比增长(YoY)推荐用日期运算JOIN而非LAG(sales,12)。

LAG 函数的基本用法与常见错误
LAG 是窗口函数,必须配合 OVER 子句使用,否则会报错 "window function not allowed here"。它不能直接用于 WHERE 或 GROUP BY,也不能在没有排序的 OVER() 中调用——否则结果不可预测。
常见误写:LAG(sales) OVER ()(缺 ORDER BY)或 LAG(sales, 1, 0) WHERE month = '2024-03'(在 WHERE 中引用窗口函数输出)。
- 必须显式指定排序:如
OVER (ORDER BY year_month),否则同一分组内行序未定义 - 若需按时间分组再比前值(比如各产品线独立环比),要加
PARTITION BY product_id - 默认取前 1 行,偏移量为
2时对应“上上期”,不是“去年同期”——后者需结合日期运算
计算月度环比增长率(MoM)
环比即本期值相比上期值的变化率,公式为 (current - previous) / previous。关键点是处理首月无前值(LAG 返回 NULL)和除零风险。
SELECT
year_month,
sales,
LAG(sales) OVER (ORDER BY year_month) AS prev_sales,
CASE
WHEN LAG(sales) OVER (ORDER BY year_month) = 0 THEN NULL
ELSE (sales - LAG(sales) OVER (ORDER BY year_month)) * 1.0 / LAG(sales) OVER (ORDER BY year_month)
END AS mom_rate
FROM monthly_sales;- 用
* 1.0强制转为浮点,避免整数除法截断(如 PostgreSQL/SQL Server 中5/2 = 2) - 不建议用
COALESCE(LAG(...), 0)填 0 再除——会导致0/0或误导性 0% 增长 - 如果数据有缺失月份(如 2024-01、2024-03 连续),
LAG取的是物理上一行,不是逻辑上“上月”,需先补全时间序列
计算同比增长率(YoY)需绕开 LAG 的局限
LAG(sales, 12) 看似能取去年同月,但仅当数据严格按月连续、无缺失且 year_month 是字符串(如 '2024-03')时才可能碰巧对齐。实际中更可靠的方式是用日期运算关联自身。
推荐做法:用 LEFT JOIN 或 LATERAL(PostgreSQL)/ APPLY(SQL Server)匹配去年同期行:
SELECT
curr.year_month,
curr.sales,
lastyear.sales AS ly_sales,
CASE
WHEN lastyear.sales IS NOT NULL AND lastyear.sales != 0
THEN (curr.sales - lastyear.sales) * 1.0 / lastyear.sales
END AS yoy_rate
FROM monthly_sales curr
LEFT JOIN monthly_sales lastyear
ON lastyear.year_month = TO_CHAR(curr.year_month::date - INTERVAL '1 year', 'YYYY-MM');- MySQL 用户可用
DATE_SUB(STR_TO_DATE(curr.year_month, '%Y-%m'), INTERVAL 1 YEAR)格式化后比较 - 若
year_month是整数(如202403),需用数学方式转换:(curr.year_month / 100 - 1) * 100 + curr.year_month % 100,但注意 202401 → 202301,而 202401 - 100 = 202301 成立,202412 - 100 = 202312 也成立——这种技巧仅适用于固定两位月份 -
LAG(sales, 12)在月度连续、无空档时可快速验证,但生产环境不建议依赖
性能与兼容性注意事项
窗口函数本身性能尚可,但嵌套多次 LAG(如同时算 MoM 和 YoY)会导致重复计算,执行计划里可能看到多个 WindowAgg 节点。
- PostgreSQL 14+ 支持
WINDOW命名复用:OVER w1定义一次,多处引用 - MySQL 8.0+、SQL Server 2017+、Oracle、Snowflake 均支持
LAG;SQLite 和旧版 MySQL 不支持 - 大数据量下,
ORDER BY字段必须有索引,否则排序成本陡增——尤其year_month上没索引时,LAG会拖慢整个查询 - 如果原始表按
year_month分区(如 ClickHouse、BigQuery),PARTITION BY配合ORDER BY能显著减少跨分区扫描
真正麻烦的从来不是写对 LAG,而是确认你的“上期”到底指什么:是数据库里上一行?日历上上个月?还是业务定义的“最近一个结算周期”?这个语义必须在建模阶段就对齐,否则后期所有环比指标都会漂移。

















