datetrunc(month, date) 返回 datetime2 类型时间戳(如'2024-03-01 00:00:00.0000000'),非字符串,支持索引与计算;误用 like 匹配将失败,应改用时间范围比较或 convert(char(7), ..., 126) 格式化展示。

DATETRUNC(month, date) 返回的是 datetime2,不是字符串
直接用 DATETRUNC('month', OrderDate) 得到的是类似 '2024-03-01 00:00:00.0000000' 的完整时间戳,类型和输入一致(比如输入是 datetime2,输出也是 datetime2),不是 '2024-03' 这种字符串。这意味着它能参与时间计算、范围查询,也能走索引——但显示上带时分秒,看着不够“干净”。
常见错误是误以为它返回字符串,然后试图用 LIKE '2024-03%' 去过滤,结果全匹配失败。正确做法是直接比较时间范围或用 CONVERT(char(7), ...) 格式化展示。
- 想查 2024 年 3 月全部记录:用
WHERE DATETRUNC('month', OrderDate) = '2024-03-01'(可走索引) - 想展示成“2024-03”:加
CONVERT(char(7), DATETRUNC('month', OrderDate), 126),注意 126 格式默认输出yyyy-mm-dd,截前 7 位刚好 - 别用
FORMAT(DATETRUNC('month', OrderDate), 'yyyy-MM'):性能差,且在高并发下可能触发 CLR 权限报错
GROUP BY 必须和 SELECT 中的表达式完全一致
SQL Server 对 GROUP BY 的表达式一致性要求严格。如果 SELECT 里写的是 DATETRUNC('month', OrderDate),那 GROUP BY 也必须一模一样,不能简写成 OrderDate 或只写 'month'。
错误示例:SELECT DATETRUNC('month', OrderDate), SUM(Amount) FROM Orders GROUP BY OrderDate → 直接报错或结果错乱。
- 正确写法:
GROUP BY DATETRUNC('month', OrderDate) - 如果还要按客户分组,得写成
GROUP BY DATETRUNC('month', OrderDate), CustomerID,不能漏掉任何非聚合字段 - 别在 SELECT 里套函数再 GROUP BY 原字段——SQL Server 不像 PostgreSQL 那样宽松
注意 week 和 iso_week 的行为差异
按月分组没问题,但一旦涉及 week 或 iso_week,结果可能出乎意料。SQL Server 默认一周从星期日开始(@@DATEFIRST = 7),所以 DATETRUNC('week', '2024-03-15') 截到的是 3 月 10 日(周日),而 DATETRUNC('iso_week', '2024-03-15') 截到的是 3 月 11 日(周一)。虽然这不影响月度分组,但如果你后续扩展到周维度,容易踩坑。
- 确认当前会话的
@@DATEFIRST值:SELECT @@DATEFIRST - 若业务强制要求 ISO 周(周一为起点),必须显式用
iso_week,不能依赖默认week -
quarter和year没这类歧义,放心用
老版本兼容性问题:DATETRUNC 只在 SQL Server 2022+ 可用
如果你的环境仍是 SQL Server 2019 或更早,DATETRUNC 函数根本不存在,执行会报错 Invalid object name 'DATETRUNC'。这时候得回退到传统写法。
- 推荐替代方案:
DATEFROMPARTS(YEAR(OrderDate), MONTH(OrderDate), 1)—— 安全、可读、支持所有日期类型 - 避免用
DATEADD(MONTH, DATEDIFF(MONTH, 0, OrderDate), 0):虽然等效,但可读性差,且对datetimeoffset支持不稳 - 别用字符串拼接:
CONVERT(varchar(7), OrderDate, 120)→ 类型丢失、无法走索引、格式受语言设置影响
iso_week 和 @@DATEFIRST 的交互最容易被忽略。










