trunc(date, 'mm') 是 oracle 中将日期截断至当月第一天的标准方法,返回 date 类型,仅支持 'mm'(不区分大小写),不推荐 to_char+to_date 组合以避免性能损耗与 nls 风险。

TRUNC(date, 'MM') 是最直接的截断方式
Oracle 的 TRUNC 函数对日期支持格式化截断,不需要转字符串再拼接。传入 'MM' 作为第二个参数,就能把任意日期归到当月第一天(时分秒全置为 0)。
常见错误是写成 'MONTH' 或 'MON' —— Oracle 只认 'MM'(大小写不敏感,但建议大写),其他字符串会报 ORA-01821: date format not recognized。
-
TRUNC(SYSDATE, 'MM')→ 当前月第一天,如2024-04-01 00:00:00 -
TRUNC(DATE '2023-12-25', 'MM')→2023-12-01 00:00:00 - 若省略第二个参数,
TRUNC(SYSDATE)默认截断到天(即清空时分秒),不是月
为什么不用 TO_CHAR + TO_DATE 组合
有人习惯先用 TO_CHAR(dt, 'YYYY-MM') 得到字符串,再用 TO_DATE(..., 'YYYY-MM') 转回去。这不仅多一次类型转换,还隐含风险:
- 依赖当前会话的
NLS_DATE_FORMAT,在某些环境可能报ORA-01843: not a valid month - 性能更差:字符串解析比纯日期运算开销大,尤其在 WHERE 条件或 GROUP BY 中高频使用时
- 丢失原始时区信息(如果字段是
TIMESTAMP WITH TIME ZONE,TRUNC仍保持时区语义;而TO_CHAR强制转为会话时区字符串再解析,易出错)
TRUNC(date, 'MM') 在查询中的典型用法
最常用于按月聚合或范围筛选。注意它返回的是 DATE 类型,可直接参与比较运算,无需额外转换。
SELECT TRUNC(order_date, 'MM') AS order_month,
COUNT(*) AS cnt
FROM orders
WHERE order_date >= TRUNC(ADD_MONTHS(SYSDATE, -6), 'MM')
GROUP BY TRUNC(order_date, 'MM')
ORDER BY order_month;
这段 SQL 获取近 6 个月(含当月)的订单月度统计。关键点:
-
ADD_MONTHS(SYSDATE, -6)先算出半年前的日期,再用TRUNC(..., 'MM')对齐到月初,确保范围严格按自然月切分 - WHERE 中用
order_date >= ...而不是TRUNC(order_date, 'MM') >= ...,避免对列函数索引失效(如果order_date上有普通 B-Tree 索引) - GROUP BY 里用
TRUNC(order_date, 'MM')是安全的,Oracle 能正确去重和分组
兼容性与边界情况提醒
TRUNC(date, 'MM') 自 Oracle 8i 起就支持,现代版本无兼容问题。但要注意两个容易被忽略的细节:
- 输入必须是
DATE或能隐式转为DATE的值(比如字符串需符合会话格式);若字段是VARCHAR2存的日期,必须先用TO_DATE显式转换,否则运行时报ORA-01722: invalid number或类型不匹配 - 对
TIMESTAMP类型也有效,结果是TIMESTAMP(精度保留,仅秒及以下归零);但若字段是TIMESTAMP WITH TIME ZONE,TRUNC会先按会话时区转换再截断,必要时应先用AT TIME ZONE标准化
真正要小心的不是语法,而是你手里的数据类型和 NLS 设置——它们往往比函数本身更容易导致意外结果。











