必须显式使用PARTITION BY分组字段和ORDER BY时间升序字段,如LAG(sales) OVER (PARTITION BY product_id ORDER BY order_month),否则会跨组混取数据;NULL值和除零需用NULLIF(prev, 0)和CASE WHEN联合防护。

LAG 函数怎么写才能拿到上期值
直接用 LAG() 取上一行数据,关键在 ORDER BY 和 PARTITION BY。如果排序错,上期值就乱了;没加 PARTITION BY 跨业务线混算,结果必然出错。
- 必须搭配
ORDER BY(比如按date升序),否则窗口函数行为未定义 - 多维度对比时(如按
product_id分别算),一定要加PARTITION BY product_id -
LAG(column, 1, 0)第三个参数是默认值,避免首行返回NULL导致后续计算中断
环比增长率公式里怎么处理除零和 NULL
用 LAG() 拿到上期值后,直接除容易报错或得 NULL:上期为 0 会触发除零错误,上期为 NULL(如首月)会让整个结果变 NULL。
- 用
NULLIF(上期值, 0)把 0 转成NULL,再配合COALESCE(..., 0)统一兜底 - 更稳妥写法:
COALESCE((本期值 - LAG(本期值) OVER (...)) / NULLIF(LAG(本期值) OVER (...), 0), 0) - 如果业务允许“首期无环比”,就别硬填 0,保留
NULL更真实
为什么用 LAG 而不是自连接或子查询
自连接写起来啰嗦、性能差,尤其数据量大时;子查询嵌套深、可读性低。而 LAG() 是单次扫描,逻辑清晰,执行计划干净。
- 自连接要写
JOIN t1 ON t1.date = t2.date - INTERVAL '1 month',日期对齐易出错 - 子查询每次都要重跑
SELECT ... WHERE date = ...,O(n²) 复杂度 -
LAG()在窗口内一次完成位移,数据库原生优化,PG/MySQL 8.0+/Oracle 都支持
MySQL 8.0 和 PostgreSQL 的细微差别
语法基本一致,但 MySQL 对 ORDER BY 要求更严格:必须是确定性排序(比如不能只用 id,若 id 不唯一,相同 id 行的 LAG 结果可能不稳定)。
- PostgreSQL 允许
ORDER BY timestamp即使有重复时间戳,内部会自动加ctid稳定排序 - MySQL 必须显式补唯一键:
ORDER BY date, id或ORDER BY date, RAND()(不推荐) - 两者都支持
LAG(value, 1),但 MySQL 不支持LAG(value, n, default)中的default参数(需用COALESCE替代)
实际跑的时候,先确认你的日期字段有没有缺失、是否按自然月连续——LAG() 只认行序,不管日历逻辑。比如跳过 2 月,3 月的“上期”会取到 1 月,不是你想的“上个月”。

















