日期時間是業務系統最高頻數據類型,亦是Bug高發區。本文系統整理TO_DATE/TO_CHAR格式元素對照表、日期運算、SYSDATE/SYSTIMESTAMP區別、NLS_DATE_LANGUAGE影響、BETWEEN邊界陷阱、分區裁剪寫法、性能優化原則,構建「零Bug日期代碼」檢查清單。
| 函數 | 方向 | 語法 | 關鍵點 |
|---|---|---|---|
TO_DATE | 字串 → DATE | TO_DATE(string, fmt [, nls]) | fmt必須與string逐字符對應;缺省fmt取決於NLS_DATE_FORMAT |
TO_CHAR | DATE → 字串 | TO_CHAR(date, fmt [, nls]) | 輸出格式完全受fmt控制;可用於排序、分組、顯示 |
TO_TIMESTAMP | 字串 → TIMESTAMP | TO_TS(string, fmt [, nls]) | 保留毫秒/微秒/時區;推薦新系統統一用TIMESTAMP WITH TIME ZONE |
| 元素 | 含義 | 示例 | 備註 |
|---|---|---|---|
| YYYY | 4位年 | 2024 | ⭐ |
| YY | 2位年 | 24 | 當前世紀 |
| MM | 月(01-12) | 01 | ⭐ |
| MON | 月縮寫 | JAN | 受NLS_DATE_LANGUAGE影響 |
| MONTH | 月全名 | JANUARY | 右填空格 |
| DD | 日(01-31) | 01 | ⭐ |
| DDD | 年內天數 | 001 | |
| DAY | 星期全名 | MONDAY | 右填空格 |
| DY | 星期縮寫 | MON | |
| HH24 | 時(00-23) | 13 | ⭐ 24制 |
| HH12 | 時(01-12) | 01 | 12制 |
| MI | 分(00-59) | 30 | ⭐ |
| SS | 秒(00-59) | 45 | ⭐ |
| FF3 | 毫秒(3位) | 123 | TIMESTAMP專用 |
| TZH:TZM | 時區偏移 | +08:00 | TZ專用 |
-- 錯誤:WHERE date_col = '2024-01-15' -- 隱式TO_DATE,fmt取決於Session NLS,可能不匹配,導致全表掃描
-- 正確:
WHERE date_col = TO_DATE('2024-01-15', 'YYYY-MM-DD')
WHERE date_col = DATE '2024-01-15' -- ANSI日期字面量,無歧義
-- 錯誤:查詢 2024-01-15 全天
WHERE created_dt BETWEEN TO_DATE('2024-01-15','YYYY-MM-DD')
AND TO_DATE('2024-01-15','YYYY-MM-DD')
-- 實際只含 2024-01-15 00:00:00 那一秒
-- 正確寫法1:半開區間 [start, end)
WHERE created_dt >= TO_DATE('2024-01-15','YYYY-MM-DD')
AND created_dt < TO_DATE('2024-01-16','YYYY-MM-DD')
-- 正確寫法2:TRUNC截取日期部分
WHERE TRUNC(created_dt) = TO_DATE('2024-01-15','YYYY-MM-DD')
-- 注意:TRUNC會導致索引失效,除非建立函數索引
-- Session為AMERICAN,解析中文月份失敗
TO_DATE('15-一月-2024', 'DD-MON-YYYY') -- ORA-01843
-- 解決:顯式指定語言
TO_DATE('15-一月-2024', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=SIMPLIFIED CHINESE')
-- 或 ALTER SESSION SET NLS_DATE_LANGUAGE = 'SIMPLIFIED CHINESE';
DATE - DATE = NUMBER (天數,含小數)DATE + NUMBER = DATE (天數,可為小數:0.5 = 12小時)INTERVAL DAY TO SECOND 類型支援精確加減 (12c+推薦)。MONTHS_BETWEEN、ADD_MONTHS、NEXT_DAY、LAST_DAY、ROUND/TRUNC(date, 'MM') 常用函數。-- 範圍分區表 sales (partition by range(sale_dt))
-- 查詢 2024年1月數據
-- ✅ 正確:分區裁剪生效
SELECT * FROM sales
WHERE sale_dt >= TO_DATE('2024-01-01','YYYY-MM-DD')
AND sale_dt < TO_DATE('2024-02-01','YYYY-MM-DD');
-- ❌ 錯誤:TRUNC導致全分區掃描
SELECT * FROM sales WHERE TRUNC(sale_dt, 'MM') = TO_DATE('2024-01','YYYY-MM');
-- ✅ 正確:虛擬欄位 + 函數索引 (11g+)
ALTER TABLE sales ADD (sale_month GENERATED ALWAYS AS (TRUNC(sale_dt, 'MM')) VIRTUAL);
CREATE INDEX idx_sales_month ON sales(sale_month);
SELECT * FROM sales WHERE sale_month = TO_DATE('2024-01','YYYY-MM');
TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF',避免格式字串解析開銷。WHERE TRUNC(dt) = ... → 改半開區間或虛擬欄位索引。FORALL i IN 1..cnt INSERT ... VALUES (dates(i))。TIMESTAMP WITH TIME ZONE,應用層統一UTC,顯示層按用戶時區轉換。METHOD_OPT => 'FOR COLUMNS SIZE 254 sale_dt',優化器精準估算基數。日期時間處理的核心原則:顯式格式、半開區間、避免隱式轉換、分區裁剪友好。將上述檢查清單納入Code Review規範,可消除90%以上日期相關Bug。