mysql的week()默认mode 0易致跨年周错乱,应使用week(date, 3)按iso标准计算;postgresql用extract(isoweek from date)和extract(isoyear from date)组合;sql server需配合datepart(iso_week, date)与推导iso年分组。

MySQL 的 WEEK() 函数默认行为容易导致跨年周错乱
MySQL 的 WEEK() 默认用 mode 0(周日为起点,第一周需含 1 月 1 日),这会让 2024-12-30(周一)和 2025-01-01(周四)被分到不同周,哪怕它们属于同一个 ISO 周(2024-W53)。实际业务中,尤其报表统计,需要按 ISO 周对齐——即周一为起点、第一周必须含当年至少 4 天的周。
正确做法是显式指定 mode:用 WEEK(date, 1)(周一为第一天)或更稳妥的 WEEK(date, 3)(周一为第一天 + 第一周需含 4 天以上,即 ISO 标准)。WEEK(date, 3) 才能保证 2024-12-30 和 2025-01-01 同属 2024 年第 53 周。
-
WEEK('2024-12-30', 3)→ 53 -
WEEK('2025-01-01', 3)→ 53(不是 1) - 别用
WEEK(date)或WEEK(date, 0),它们把 2025-01-01 算作 2025 年第 1 周
PostgreSQL 中用 EXTRACT(ISOWEEK FROM ...) 最可靠
PostgreSQL 原生支持 ISO 周标准,EXTRACT(ISOWEEK FROM date) 直接返回 1–53 的周数,且自动关联对应 ISO 年(EXTRACT(ISOYEAR FROM date))。不需要手动处理跨年逻辑。
-
EXTRACT(ISOWEEK FROM '2024-12-30'::date)→ 53 -
EXTRACT(ISOYEAR FROM '2024-12-30'::date)→ 2024 -
EXTRACT(ISOWEEK FROM '2025-01-01'::date)→ 53 -
EXTRACT(ISOYEAR FROM '2025-01-01'::date)→ 2024(关键!不能只取年份字段)
分组时必须同时用 ISOYEAR 和 ISOWEEK,否则 2024-W53 和 2025-W01 会混在一起。
SQL Server 的 DATEPART(ISO_WEEK, ...) 需配合 YEAR() 调整
SQL Server 的 DATEPART(ISO_WEEK, date) 返回 ISO 周号没错,但它不提供 ISO 年。而 YEAR(date) 返回日历年的年份,对 2025-01-01 这类日期会返回 2025,但它的 ISO 年其实是 2024。
得自己推导 ISO 年:当周数为 1 且月份为 12 月时,ISO 年 = 日历年 - 1;当周数 ≥ 52 且月份为 1 月时,ISO 年 = 日历年 + 1;其余情况等于日历年。更稳的方式是用 DATEPART(ISO_WEEK, date) 加上一个计算 ISO 年的表达式:
DATEPART(YEAR, DATEADD(day, 26 - DATEPART(ISOWEEK, date), date))
这个技巧利用了“每年第 26 周的某天必然落在该 ISO 年内”的特性,比条件判断更简洁可靠。
跨年周分组时最容易忽略的坑:只按周数分组,不绑定年份
无论哪种数据库,只要只用 WEEK(date, 3) 或 EXTRACT(ISOWEEK FROM date) 分组,都会把 2024-W01 和 2025-W01 合并——因为周数都是 1。这是最常见也最隐蔽的错误。
- MySQL:必须组合
YEAR(DATE_SUB(date, INTERVAL WEEKDAY(date) DAY))或更推荐用YEARWEEK(date, 3)(返回 YYYYWW 格式整数,如 202453) - PostgreSQL:必须写
EXTRACT(ISOYEAR FROM date), EXTRACT(ISOWEEK FROM date)两个字段一起 GROUP BY - SQL Server:必须用推导出的 ISO 年 +
DATEPART(ISO_WEEK, date)
真正要对齐业务口径的“自然周”,核心不是算周几,而是确认那一周属于哪个 ISO 年——这个年份可能和日期本身的年份不同。










