直接套用AVG() + OVER()算不出正确同比,因为同比需跨行比较(如2024-05与2023-05),而AVG()是聚合函数,不提供行间引用能力;正确做法是先按年月GROUP BY聚合,再用LAG(sales, 12) OVER (ORDER BY year_mon)获取去年同期值,最后计算差值与比率。

为什么直接套用 AVG() + OVER() 算不出正确同比?
因为同比(如“2024年5月 vs 2023年5月”)本质是跨行比较,不是简单分组聚合。窗口函数本身不改变行数,但若误用 GROUP BY 先聚合再开窗,会丢失明细时间粒度,导致 LAG() 找不到上一年同月的值。常见错误现象:LAG(sales, 12) OVER (ORDER BY dt) 返回 NULL——不是函数写错了,而是 dt 缺少连续、无缺失的月度记录,或原始数据未按自然月对齐(比如混入了日粒度且未归约)。
实操建议:
- 先确保时间字段是标准月首/月尾(如用
DATE_TRUNC('month', order_time)或TO_CHAR(order_time, 'YYYY-MM')统一生成ym字段) - 必须保留原始明细或至少按
ym做一次聚合(SUM(sales)),再以此结果集为输入进行窗口计算 - 慎用
ROWS BETWEEN,同比依赖的是逻辑位移(LAG(value, 12)),不是物理行偏移
LAG() 和 LEAD() 在环比中怎么选参数?
环比看相邻周期,通常用 LAG(value, 1) 拿上期值。但关键在排序字段——必须用可排序且无跳跃的时间代理键。例如用 EXTRACT(YEAR FROM dt)*100 + EXTRACT(MONTH FROM dt) 生成 ym_code(如 202405),再 ORDER BY ym_code。若直接 ORDER BY dt 且 dt 是日期类型,可能因某月无数据导致 LAG() 跳到上上月。
实操建议:
- 避免用字符串型年月(如
'2024-05')直接排序,'2024-10' 会排在 '2024-2' 前面 - 如果要算周环比,把
LAG(value, 1)换成LAG(value, 1) OVER (ORDER BY year_week),其中year_week是EXTRACT(YEAR FROM dt)*100 + EXTRACT(WEEK FROM dt) - 使用
COALESCE(LAG(...), 0)处理首期无对比值的情况,比留 NULL 更利于后续除法计算
同比计算中除零和 NULL 怎么安全处理?
写 (cur_value - last_year_value) / last_year_value 时,last_year_value 为 0 或 NULL 会导致结果为 NULL 或报错(如 PostgreSQL 的 division by zero)。这不是语法问题,而是业务逻辑断点。
实操建议:
- 统一用
NULLIF(last_year_value, 0)替换分母,使除零转为 NULL,再用COALESCE(..., 0)或CASE WHEN显式标注(如CASE WHEN last_year_value = 0 THEN 'new_item' ELSE ... END) - 不要在窗口函数内部做除法,先算出
cur_value和last_year_value两列,再在外层 SELECT 中计算变化率——便于调试和复用 - 若需百分比展示,用
ROUND(100.0 * delta / NULLIF(last_year_value, 0), 2),注意100.0强制浮点运算,避免整数截断
PostgreSQL 和 MySQL 8.0+ 在写法上有哪些关键差异?
核心逻辑一致,但细节易踩坑。MySQL 不支持 DATE_TRUNC(),得用 STR_TO_DATE(CONCAT(YEAR(dt), '-', MONTH(dt), '-01'), '%Y-%m-%d');PostgreSQL 的 EXTRACT(MONTH FROM ...) 返回整数,MySQL 需 MONTH(dt)。另外,MySQL 对 OVER 子句括号更敏感——LAG(sales) OVER (ORDER BY ym) 合法,但 LAG(sales, 1) OVER ORDER BY ym 少了括号就报错。
实操建议:
- MySQL 8.0+ 必须显式写
OVER (ORDER BY ...),不能省略括号;PostgreSQL 允许OVER ()表示全局窗口,但同比场景下基本不用 - 两者都支持
FIRST_VALUE()/LAST_VALUE(),但默认LAST_VALUE()行为不同(MySQL 默认RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,PG 默认ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),同比不推荐用这两个函数替代LAG() - 测试时优先用小数据集验证时间序列连续性:查
SELECT COUNT(DISTINCT ym) FROM t和理论月份数是否一致
实际跑通的关键,往往卡在时间维度没对齐——不是函数不会用,是上游数据里缺了某个月的聚合行,或者年月字段类型混乱。动手前先 SELECT ym, SUM(sales) FROM t GROUP BY ym ORDER BY ym 扫一眼,比反复调 LAG() 参数更省时间。

















