mysql的week()函数默认模式0易致跨年周数错乱,应统一使用yearweek(date,5)按iso标准处理,避免拼接year()+week(),并建生成列索引优化查询。

MySQL的WEEK()函数默认行为容易导致跨年周数错乱
MySQL的WEEK()函数默认使用模式0(即WEEK(date,0)),它把每年1月1日所在周当作第1周,且该周必须包含周四才算作当年第1周——但这个规则在年末/年初交接时极易引发“同一日期被算进两个不同年份的第1周”或“12月最后一周被归为下一年第1周”的问题。比如2023-12-31是周日,按模式0可能返回1,年份却是2024,造成业务统计口径断裂。
常见错误现象:SELECT WEEK('2023-12-31'), YEAR('2023-12-31') 返回 1 和 2023,但实际该周多数日期属于2024年;或者用YEARWEEK()时没指定模式,导致跨年周被截断到错误年份。
- 必须显式指定
WEEK()的第二个参数,推荐用模式5(ISO标准):WEEK(date,5)—— 周一为每周第一天,且第1周必须含至少4天在当年内 -
YEARWEEK(date,5)比拼接YEAR()+WEEK()更可靠,它返回一个整数(如202352),天然绑定年份与周数 - 避免用
WEEKOFYEAR(),它等价于WEEK(date,0),无法规避跨年歧义
用YEARWEEK()替代拼接年份+周数的写法
直接拼接YEAR(date)和WEEK(date)看似直观,但跨年时WEEK()可能返回1而YEAR()仍是上一年,导致“2023-12-31”被标为“2023年第1周”,逻辑错误。
正确做法是统一用YEARWEEK()并固定模式:
SELECT
'2023-12-31' AS dt,
YEARWEEK('2023-12-31', 5) AS yw_iso, -- 返回 202401(ISO标准下属2024年第1周)
YEARWEEK('2023-12-31', 0) AS yw_default; -- 返回 202301(易误导)
- 模式5(ISO)下,
YEARWEEK('2023-12-31',5)=202401,符合ISO 8601:2023-12-31属于2024年第1周 - 若业务强制要求“日历年起始周”,才考虑模式1(周一为始,第1周含1月1日),但需同步确认报表口径是否接受2023-12-25~2023-12-31被划入2023年第52周而非2024年第1周
- 注意
YEARWEEK()返回的是整数,做范围查询时别漏掉前导零处理(如202401≠'202401',但比较大小无影响)
GROUP BY周统计时务必用YEARWEEK(date,5)
做周粒度聚合时,如果只按WEEK(date)分组,2023-12-31和2024-01-01可能落在同一周但被拆到不同年份的统计桶里;反之,若只按YEAR(date)分组,又会把跨年周切碎。
正确示例(按ISO周聚合订单):
SELECT YEARWEEK(created_at, 5) AS week_id, COUNT(*) AS order_cnt FROM orders WHERE created_at >= '2023-12-01' GROUP BY YEARWEEK(created_at, 5) ORDER BY week_id;
- 结果中
week_id为202352、202401、202402……连续且无歧义 - 不要用
DATE_SUB(created_at, INTERVAL WEEKDAY(created_at) DAY)算周一日期再分组,既慢又不解决年份归属问题 - 索引优化:若高频按周查询,可添加生成列
ALTER TABLE orders ADD COLUMN yw INT AS (YEARWEEK(created_at,5)) STORED,再对yw建索引
应用层解析YEARWEEK()结果时注意格式转换
MySQL返回的YEARWEEK()是整数(如202401),但前端或BI工具常需“2024-W01”这类字符串,直接CONVERT(YEARWEEK(...), CHAR)可能出错——比如20241会被误读为“2024年第1周”而非“2024年第01周”。
- 安全转字符串:
LPAD(YEARWEEK(date,5), 6, '0')→'202401',再用SUBSTR分段 - 提取年份:
FLOOR(YEARWEEK(date,5)/100);提取周数:YEARWEEK(date,5) % 100 - 警惕时区影响:
YEARWEEK()基于会话时区计算,确保连接层SET time_zone = '+00:00'或统一使用UTC时间存储
跨年周数本质不是MySQL缺陷,而是ISO标准与本地习惯的冲突点。选模式5并全程用YEARWEEK(),比事后修补字段拼接可靠得多。真正麻烦的是历史数据已用错模式入库,这时候改逻辑不如重建带正确yw字段的视图来得干脆。











