跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

SQL如何计算同比增长率_日期偏移与自关联聚合计算

同比计算应优先使用DATE_SUB或INTERVAL进行日期偏移,避免字符串拼接;需确保业务周期对齐(如用period_id而非年月加减),并用CASE WHEN处理分母为0或NULL的情况。 用 DATE_SUB 或 INTERVAL 做同比日期偏移,别硬写字符串拼接 同比就是“今年某月 vs 去年同月”,核心是把当前日期减去 1 年。MySQL 里最稳的方式是
DATE_SUB(curdate(), INTERVAL 1 YEAR)
,PostgreSQL 用
current_date - INTERVAL '1 year'
。硬拼
CONCAT(YEAR(date_col)-1, '-', MONTH(date_col), '-', DAY(date_col))
看似直观,但遇到 2 月 29 日或月末(如 3 月 31 日 → 2 月 29 日不存在)会直接报错或结果错乱。 实操建议: 优先用数据库原生日期运算函数,不是字符串操作 如果字段是
datetime
类型,偏移后注意时区和精度,必要时用
DATE()
截断时间部分 在 WHERE 条件里做偏移时,确保该列有索引,否则
DATE_SUB(sale_date, INTERVAL 1 YEAR)
无法走索引 自关联聚合算同比增长率:ON 条件必须对齐业务周期 常见错误是写成
ON t1.month = t2.month AND t1.year = t2.year + 1
——这只能对齐“自然月”,但实际业务可能按财年、滚动 30 天、或销售周期(比如 4-4-5 周历)。真正要对齐的是“同一业务周期标识”,比如你有一张
dim_period
表带
period_id
和
is_current
字段,那关联条件应该是
ON t1.period_id = t2.last_year_period_id
。 实操建议: 不要依赖年份/月份字段直接加减,先确认业务上“同比”定义是否和日历一致 若无维度表,至少用
YEARWEEK(sale_date, 1)
(MySQL)对齐周粒度,避免跨周偏差 LEFT JOIN 后记得用
COALESCE(t2.amount, 0)
处理去年无数据的情况,否则除零或 NULL 会让整行消失 计算公式里分母为 0 或 NULL 的坑必须显式处理 同比增长率公式是
(this_year - last_year) / last_year
。一旦
last_year
是 0 或 NULL,结果就是
NULL
或报错(如 PostgreSQL 的 division by zero)。这不是数学问题,是 SQL 执行逻辑问题。 实操建议: 永远用
CASE WHEN COALESCE(last_year, 0) = 0 THEN NULL ELSE (this_year - last_year) / last_year END
别信
NULLIF(last_year, 0)
——它只解决除零,不解决 NULL 分母;
COALESCE
更可控 如果业务要求“去年为 0 时显示 100%”,那就明确写
WHEN last_year = 0 AND this_year > 0 THEN 1.0
窗口函数替代自关联?小心数据膨胀和排序依赖 有人想用
LAG(amount) OVER (PARTITION BY product_id ORDER BY sale_month)
算同比,但 LAG 默认是“上一行”,不是“去年同期”。除非你提前把数据按年月补全(比如用
generate_series
或递归 CTE),否则 LAG 拿到的大概率是上个月,不是去年同月。 实操建议: LAG/LEAD 适合环比,不适合同比;强行用就得先保证每期数据严格存在、且排序字段能唯一映射到“去年同周期” 如果真要用窗口函数,得配合
ROW_NUMBER() OVER (PARTITION BY product_id, YEAR(sale_date) ORDER BY sale_date)
再做二次匹配,复杂度远超自关联 大表慎用自关联+窗口混用,容易触发临时表磁盘溢出,尤其在 MySQL 8.0 以下 同比看着简单,实际卡点全在“周期对齐”和“空值防御”上。业务口径没对齐,代码写得再漂亮,结果也是错的;分母没兜底,报表一跑就断。

相关文章