WEEK()跨年跳变因按年切片计算且无年份上下文,导致2024-12-30与2024-12-29分属不同年份的周;应改用YEARWEEK()配合mode参数(推荐mode=1)实现ISO兼容的「年+周」组合分组与统计。

WEEK() 函数返回的周数为什么跨年会跳变?
MySQL 的 WEEK() 默认使用模式 0(周日为一周开始,第1周需包含1月1日),这导致 2024-12-30(周一)可能被算作 2025 年第 1 周,而 2024-12-29(周日)反而是 2024 年第 52 周——不是“连续递增”,而是按年切片计算。本质是 WEEK() 不带年份上下文,纯按日期推算,不保证跨年连续性。
常见错误现象:GROUP BY WEEK(order_date) 会让 2024-12-30 和 2025-01-01 落在同一个分组(比如都返回 1),实际却分属不同自然年。
- 用
WEEK(date, mode)时务必显式指定mode,别依赖默认值;mode=1(周一为始,第1周含该年首个周四)更接近 ISO 标准,但依然不解决跨年歧义 -
WEEKOFYEAR()是WEEK(date, 3)的别名,同样无年份绑定,不能单独用于跨年统计 - 真正需要的是「年+周」组合,而不是孤立的周数
正确做法:用 YEARWEEK() 替代 WEEK() 做分组基准
YEARWEEK() 返回一个整数(如 202452、202501),隐含年份信息,天然规避跨年混淆。它默认 mode=0,但关键在于结果不可拆分——你得把它当整体用。
实操建议:
- 分组时直接写
GROUP BY YEARWEEK(order_date, 1)(推荐 mode=1,ISO 兼容性更好) - 若需展示“2024-W52”这类格式,用
CONCAT(YEAR(order_date), '-W', LPAD(WEEK(order_date, 1), 2, '0'))——但注意:这仍可能出错,因为YEAR()和WEEK()各自取年、各算各的周 - 安全展示法:先算
YEARWEEK(date, 1),再用整数运算拆解,例如FLOOR(yw / 100)得年份,yw % 100得周数(yw是YEARWEEK()结果)
mode 参数差异直接影响跨年边界判断
同一个日期,不同 mode 下的 WEEK() 或 YEARWEEK() 结果可能差 1 周。例如 2024-12-30:
-
YEARWEEK('2024-12-30', 0)→ 202452(按周日始,1月1日所在周为第1周) -
YEARWEEK('2024-12-30', 1)→ 202501(按周一始,且要求第1周含该年首个周四 → 2024-12-30 属于 2025 年第1周) -
YEARWEEK('2024-12-30', 3)→ 202501(同 mode=1,但第1周必须含1月4日)
选 mode 的核心逻辑:如果报表按“自然业务周”(周一到周日)且需严格 ISO 8601 对齐(如财务、国际协作),用 mode=1;如果内部系统习惯“周日为始+1月1日必在第1周”,用 mode=0。混用 mode 会导致历史数据无法对齐。
WHERE 条件中避免用 WEEK() 做跨年范围筛选
写 WHERE WEEK(order_date) BETWEEN 50 AND 5 想查“年末到年初”的数据?这会漏掉所有周数
正确方式只有两种:
- 用日期范围:
WHERE order_date BETWEEN '2024-12-01' AND '2025-01-31'(最安全) - 用
YEARWEEK()数值范围:WHERE YEARWEEK(order_date, 1) BETWEEN 202450 AND 202505(需确认起止年周是否真实存在)
注意:YEARWEEK() 计算结果是整数,比较时别加引号,否则可能触发字符串隐式转换导致索引失效。
跨年周统计真正的复杂点不在函数本身,而在于业务定义是否明确——你的“第1周”到底以什么为准?一旦定义模糊,再怎么调 mode 都只是迁就错误前提。数据库只负责忠实执行规则,不负责解释“合理”。










