周同比是对比同一自然周的去年值,需用YEARWEEK(date,1)按ISO标准对齐年周编号,而非简单DATE_SUB减7天,否则年初年末易错位1–2天。

什么是周同比,以及为什么不能直接用 DATE_SUB 算上周同一日
周同比不是简单拿“今天减7天”的数据对比,而是要对齐自然周:比如2024-06-10(周一)属于第24周,它的同比应是2023-06-12(也是周一,且同为第24周)。直接用 DATE_SUB(order_date, INTERVAL 7 DAY) 会跨周错位,尤其在年初/年末或遇到闰年时,偏差可达1–2天。
正确做法是统一按 ISO 周标准(周一为每周第一天,第1周包含当年第一个周四),用 YEARWEEK(date, 1) 提取年+周编号,再关联去年同周。
如何建视图让周同比查询变“即查即得”
核心思路:视图不存数据,只封装逻辑。把原始销售表(如 sales)按周聚合 + 补全去年同周销售额,封装成可复用的视图。
- 必须用
YEARWEEK(order_date, 1)而非WEEK(),后者默认模式不保证ISO标准 - 用
LEFT JOIN关联自身,条件是t1.yw = t2.yw + 100(因为 202424 − 100 = 202324),比字符串拼接更高效 - 聚合时保留
year和week字段,避免后续再解析YEARWEEK值
CREATE VIEW sales_weekly_yoy AS
SELECT
YEARWEEK(s.order_date, 1) AS yw,
YEAR(s.order_date) AS year,
WEEK(s.order_date, 1) AS week,
s.product_id,
SUM(s.amount) AS curr_week_amt,
COALESCE(prev.amt, 0) AS last_year_week_amt
FROM sales s
LEFT JOIN (
SELECT
YEARWEEK(order_date, 1) AS yw,
product_id,
SUM(amount) AS amt
FROM sales
GROUP BY YEARWEEK(order_date, 1), product_id
) prev ON s.product_id = prev.product_id AND YEARWEEK(s.order_date, 1) = prev.yw + 100
GROUP BY YEARWEEK(s.order_date, 1), s.product_id;查视图时怎么快速算出同比变化率且不出错
直接在 SELECT 中计算 (curr_week_amt - last_year_week_amt) / NULLIF(last_year_week_amt, 0),关键在 NULLIF —— 如果去年同周没销量,分母为0会导致整个字段变 NULL,而 NULLIF(x, 0) 会把0转成 NULL,使除法结果也为 NULL,避免报错。
- 不要用
IF(last_year_week_amt = 0, 0, ...),它会把0当成有效基数,导致错误正增长 - 如果业务要求“去年无数据视为100%增长”,才改用
IFNULL(last_year_week_amt, 0)配合条件判断 - 注意时区:确保
order_date是 DATETIME 类型且已转为业务所在时区,否则YEARWEEK可能跨天
为什么加索引后视图查询还是慢?
MySQL 视图本身不支持索引,所谓“给视图加索引”实际是给底层表加。必须在 sales(order_date, product_id) 上建联合索引,顺序不能颠倒:日期字段在前,才能支撑 YEARWEEK(order_date, 1) 的范围扫描。
- 错误索引:
INDEX(product_id, order_date)—— 无法加速按周分组 - 正确索引:
INDEX(order_date, product_id),并确认order_date是 NOT NULL - 如果数据量超千万,考虑分区:按
YEAR(order_date)分区,避免每次扫描全表
真正容易被忽略的是 YEARWEEK() 函数的确定性 —— 它依赖系统时区和 SQL 模式。上线前务必在目标环境执行 SELECT YEARWEEK('2023-01-01', 1) 验证返回值是否为 202252(因2023-01-01是周日,属2022年第52周),否则视图结果会系统性偏移。

















