DATEPART(WEEK, date) 返回依赖 DATEFIRST 设置的周数,非 ISO 标准;应使用 DATEPART(ISO_WEEK, date) 获取符合 ISO 8601 的周数,且需配合 ISO_YEAR 确保年份归属正确。

DATEPART(WEEK, date) 返回的是哪一种周数?
SQL Server 的 DATEPART(WEEK, date) 默认返回的是“一年中的第几周”,但它的计算依赖于当前会话的 DATEFIRST 设置(决定一周从周几开始)和年份起始规则。它不等同于 ISO 8601 周数(即以周一为每周起点、且第1周必须包含该年至少4个星期四),也不等同于按自然日历简单切分的“第1–53周”。比如,2024-01-01 是周一,DATEPART(WEEK, '2024-01-01') 在默认 DATEFIRST = 7(周日为一周开始)下返回 1;但如果 DATEFIRST = 1(周一为起点),结果仍是 1 —— 看似一致,但边界日期(如2023-12-31)就可能跨周归属不同。
如何确保得到 ISO 周数(ISO_WEEK)?
SQL Server 2008 及以后版本支持 ISO_WEEK 参数,这才是真正符合 ISO 8601 标准的周数提取方式:
SELECT DATEPART(ISO_WEEK, '2023-12-31') -- 返回 52(不是 1) SELECT DATEPART(ISO_WEEK, '2024-01-01') -- 返回 1 SELECT DATEPART(ISO_WEEK, '2024-12-31') -- 返回 1(属于 2025 年第 1 周)
关键点:
-
ISO_WEEK自动按 ISO 规则对齐:每年第1周是包含该年第一个周四的那一周,且始终以周一为每周起点 - 不受
SET DATEFIRST影响,行为稳定可预期 - 注意:返回值范围是 1–53,不存在 0 或 54
DATEPART(WEEK, ...) 和 DATEPART(ISO_WEEK, ...) 混用会出什么问题?
常见错误是把两者当成等价替换,导致报表中周数错位,尤其在跨年日期附近。例如:
-- 在 DATEFIRST = 7(默认)下: SELECT DATEPART(WEEK, '2023-12-31') -- 返回 1(被算作 2024 年第 1 周) SELECT DATEPART(ISO_WEEK, '2023-12-31') -- 返回 52(正确归属 2023 年) <p>-- 同样,'2024-01-01': SELECT DATEPART(WEEK, '2024-01-01') -- 返回 1 SELECT DATEPART(ISO_WEEK, '2024-01-01') -- 也返回 1(巧合一致,但不可依赖)
所以:
- 做同比/环比、周粒度聚合时,必须统一使用
ISO_WEEK,否则 2023-12-31 和 2024-01-01 可能被分到同一“WEEK”但实际不属于同一 ISO 周 - 如果业务明确要求“从周日开始计周”,才考虑
WEEK+ 显式SET DATEFIRST 7,但需全程控制会话上下文 - 在视图或函数中硬编码
WEEK而不声明DATEFIRST,极易引发环境迁移后的逻辑漂移
提取年份+ISO周数用于分组或分区的典型写法
单纯取周数不够,通常需要“年份+周数”组合来唯一标识一周(例如 2024-W01)。推荐方式:
SELECT
YEAR(date_col) AS year_num,
DATEPART(ISO_WEEK, date_col) AS iso_week,
FORMAT(date_col, 'yyyy-') + RIGHT('0' + CAST(DATEPART(ISO_WEEK, date_col) AS VARCHAR(2)), 2) AS year_week_iso
FROM your_table注意点:
- 不要用
YEAR(date_col)直接拼 ISO 周——因为 ISO 第1周可能落在上一年的12月,比如 2024-01-01 属于 2024 年第1周,但 2023-12-31 属于 2023 年第52周;而 2025-01-01 属于 2025 年第1周,但 2024-12-30 属于 2025 年第1周(即 ISO 年份 ≠ 日历年份) - 真要严格按 ISO 年+周组合,得用
DATEPART(ISO_YEAR, date_col)(SQL Server 2016+),否则需自行实现逻辑判断 - 在 WHERE 或 GROUP BY 中直接写
DATEPART(ISO_WEEK, date_col)没问题,但涉及索引时,记得给该表达式建计算列并索引,否则无法走索引扫描
ISO 周数的“年份归属”和“周数计算”是绑定的,拆开处理(比如用 YEAR() 搭配 ISO_WEEK)大概率出错,这点在跨年数据切片时最容易被忽略。

















