lag()必须带order by,否则结果随机;多记录同天需二级排序;环比计算须用nullif防除零、case兜底并用100.0避免整除;分组场景必加partition by;lag(...,12)非同比,需补全时间维度。

LAG()取上期值必须带ORDER BY,否则结果随机
不写ORDER BY的LAG()在MySQL 8.0里不会报错,但返回值完全不可预测——窗口函数没有排序就等于没定义“上期”。必须明确指定时间字段升序,比如ORDER BY report_date。如果原始数据是日粒度但有多条同一天记录,得加二级排序:例如ORDER BY report_date, id,否则LAG可能取到同一天另一条无关记录。
环比增长率公式里除零和NULL必须显式拦截
直接写(cur - LAG(cur)) / LAG(cur)会崩:首行LAG返回NULL,整列变NULL;上期值为0则触发除零错误。安全写法是先用NULLIF(LAG(cur), 0)把分母为0转成NULL,再用CASE兜底:
ROUND(
CASE
WHEN LAG(sales) OVER (ORDER BY yyyy_mm) = 0 THEN NULL
ELSE (sales - LAG(sales) OVER (ORDER BY yyyy_mm)) * 100.0 / NULLIF(LAG(sales) OVER (ORDER BY yyyy_mm), 0)
END,
2
) AS mom_pct
注意:MySQL中100 / 10是整数除法,必须写100.0或强制转DECIMAL,否则小数位被截断。
按业务维度分组时PARTITION BY不能漏
比如算各门店的日销售额环比,漏掉PARTITION BY store_id会导致A店的今天去拿B店的昨天当“上期”。正确结构是:
LAG(daily_sales) OVER (PARTITION BY store_id ORDER BY sale_date)
常见错误场景:
- 多门店共用一张表,但没加
PARTITION BY - 按产品线分析,却只按时间排序,没按
product_line分区 - 时间字段用了
MONTH(sale_date)而非DATE_FORMAT(sale_date, '%Y-%m'),导致2025-12和2026-01排序错乱
LAG(sales, 12) ≠ 同比,缺月时结果错位
LAG(sales, 12)只是取前12行,不是“去年同月”。如果2025年7月数据缺失,2026年7月调用LAG(sales, 12)会落到2025年6月,而非真正的同比基准。真正健壮的同比必须先补全时间维度(如用日历表LEFT JOIN),或至少校验日期差:
LAG(sales) FILTER (WHERE LAG(report_date) OVER (ORDER BY report_date) = DATE_SUB(report_date, INTERVAL 12 MONTH))
MySQL不支持FILTER,所以更现实的做法是:先生成完整月序列,再JOIN对齐,否则LAG(..., 12)只能用于已确认无断层的高质量月度快照。
实际执行时最容易被忽略的是数据质量本身——窗口函数再精准,也救不了缺失月份、重复时间戳或未归一化的时间字段。别急着套公式,先用SELECT DISTINCT yyyy_mm ORDER BY yyyy_mm扫一眼时间序列是否连续。











