LAG函数必须配合OVER(ORDER BY时间字段)才能正确获取上期值,否则结果不可控;存在同天数据时需加二级排序如ORDER BY dt, id;首行返回NULL,计算增长率须用NULLIF和COALESCE或CASE WHEN处理除零与NULL。

LAG函数怎么写才能拿到上期值
直接用 LAG() 不加参数,默认取前1行,但必须配合 ORDER BY 才能保证“上期”逻辑正确;没排序或排序字段不稳定(比如时间有重复、缺失),LAG() 返回的就不是你想要的上期值。
- 必须在窗口定义中显式写
ORDER BY 时间字段,推荐用DATE或TIMESTAMP类型,避免用字符串日期 - 如果存在多条同一天的数据,需补排序次级条件,例如
ORDER BY dt, id,否则同天行间顺序不确定 -
LAG(col, 1)和LAG(col)等价,但显式写1更易读;想跳过2期就写LAG(col, 2) - 注意 NULL 处理:首行无上期,默认返回
NULL,后续计算增长率时会传染为NULL
环比增长率公式里除零和NULL怎么防
用 LAG() 拿到上期值后,直接写 (本期 - 上期) / 上期 很容易报错或得 NULL —— 上期为 0 会除零,上期为 NULL 会让整条结果变 NULL。
- 优先用
NULLIF(上期值, 0)把除零转成NULL,再配合COALESCE()统一兜底,例如:COALESCE((curr - prev) / NULLIF(prev, 0), 0) - 更稳妥的做法是加
CASE WHEN判断:CASE WHEN prev IS NULL OR prev = 0 THEN 0 ELSE (curr - prev) / prev END - 如果业务要求“首期不计算”,那就别兜底,保留
NULL,后续应用层或报表工具可识别为空白
分区计算(如按产品线分别算环比)要注意什么
实际场景常需“每个产品线各自算环比”,这时必须加 PARTITION BY,否则所有数据混在一起排,A产品的1月会去拉B产品的12月作上期。
- 窗口子句要完整:例如
OVER (PARTITION BY product_id ORDER BY dt) -
PARTITION BY字段必须和业务维度一致;若漏写,或写了错误字段(如用user_id代替product_id),结果完全不可信 - 分区后各组独立排序,每组首行仍为
NULL,这点和不分区一致 - 注意数据倾斜:某产品线数据极少(如只有1条),那它永远没有环比值,属正常现象,不是SQL写错了
不同数据库对LAG的支持差异有哪些
LAG() 是标准窗口函数,主流数据库都支持,但细节行为略有不同。
- PostgreSQL、Snowflake、BigQuery、StarRocks:完全支持
LAG(expr, offset, default)三参数形式,default可填任意默认值(如0或0.0) - MySQL 8.0+:支持三参数,但
default值类型必须和expr严格一致,否则隐式转换失败 - Trino/Presto:支持,但
default参数不能省略为NULL,必须显式写出(如LAG(x, 1, 0)) - ClickHouse:不支持标准
LAG(),要用neighbor()模拟,且无法跨分区,需谨慎
真正容易被忽略的是:即使语法通过,如果底层执行引擎不支持窗口函数下推(比如某些老版本 Hive + Tez 组合),查询可能静默退化为 MapReduce 全局排序,性能骤降。上线前务必在真实数据量级上验证执行计划。

















