日期範圍查詢是業務系統最高頻 SQL 模式之一。Oracle 提供兩種直觀寫法:轉換為 DATE 型別比較與轉換為字串比較。本文深入分析兩者語義差異、索引使用條件、隱性轉換陷阱與最佳實踐。
SELECT *
FROM mytable
WHERE mydate < TO_DATE('2000-01-05', 'YYYY-MM-DD');
mydate 欄位型別為 DATE 或 TIMESTAMP。TO_DATE 顯式轉換為日期型別,雙方同型別比較。mydate 有 B-tree 索引,優化器可做 INDEX RANGE SCAN。
SELECT *
FROM mytable
WHERE TO_CHAR(mydate, 'YYYY-MM-DD') < '2000-01-05';
TO_CHAR 轉為字串,右側為字串常數。CREATE INDEX idx ON mytable (TO_CHAR(mydate, 'YYYY-MM-DD')),否則全表掃描。'2000-1-5' < '2000-01-05' (字串逐字元比較) 可能產生非預期結果。
-- 寫法三:隱性轉換 (極度不推薦)
SELECT * FROM mytable WHERE mydate < '2000-01-05';
NLS_DATE_FORMAT 會話參數隱性轉換字串為日期。NLS_DATE_FORMAT = 'DD-MON-RR',字串 '2000-01-05' 解析失敗或解析錯誤。| 寫法 | 執行計劃 | 耗時 | 索引使用 |
|---|---|---|---|
| TO_DATE 顯式轉換 | INDEX RANGE SCAN | 0.02 秒 | ✅ |
| TO_CHAR 轉字串 | FULL TABLE SCAN | 3.5 秒 | ❌ (除非函數索引) |
| 隱性轉換 | FULL TABLE SCAN / 視 NLS 而定 | 不穩定 | ❌ |
WHERE mydate >= TO_DATE('2024-01-01','YYYY-MM-DD') AND mydate < TO_DATE('2024-02-01','YYYY-MM-DD')。BETWEEN 邊界歧義,利於分區裁剪。TO_CHAR、TRUNC、EXTRACT 皆導致索引失效。NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS,但仍不可依賴隱性轉換。
CREATE TABLE sales (
sale_date DATE,
amount NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2023_q1 VALUES LESS THAN (TO_DATE('2023-04-01','YYYY-MM-DD')),
PARTITION p_2023_q2 VALUES LESS THAN (TO_DATE('2023-07-01','YYYY-MM-DD')),
...
);
-- 查詢
SELECT * FROM sales WHERE sale_date >= TO_DATE('2023-05-01','YYYY-MM-DD')
AND sale_date < TO_DATE('2023-06-01','YYYY-MM-DD');
-- 優化器自動 Partition Pruning,僅掃描 p_2023_q2