查詢 2011-05-02 至 2011-05-30 之間的資料,`time` 欄位型別為 `DATE`(含時分秒)。
SELECT * FROM tablename
WHERE time >= TO_DATE('2011-05-02', 'YYYY-MM-DD')
AND time <= TO_DATE('2011-05-30', 'YYYY-MM-DD');
實測結果: 可查到 5/2 00:00:00 的資料,**但查不到 5/30 當天任何資料**。
原因: `TO_DATE('2011-05-30', 'YYYY-MM-DD')` 隱性轉為 `2011-05-30 00:00:00`。`<=` 此值僅包含 5/30 午夜 0 點那一瞬間。
| 修正寫法 | 說明 |
|---|---|
time < TO_DATE('2011-05-31', 'YYYY-MM-DD') | 改用「小於隔天 0 點」,涵蓋 5/30 全天 |
time <= TO_DATE('2011-05-30 23:59:59', 'YYYY-MM-DD HH24:MI:SS') | 明確指定當天最後一秒 |
time <= TO_DATE('2011-05-30', 'YYYY-MM-DD') + 0.99999 | 利用 DATE 算術加 0.99999 天 (≈ 23:59:59) |
SELECT * FROM tablename
WHERE TO_CHAR(time, 'YYYY-MM-DD') >= '2011-05-02'
AND TO_CHAR(time, 'YYYY-MM-DD') <= '2011-05-30';
實測結果: 同時查到 5/2 與 5/30 全天資料,邏輯直觀符合人類思維。
| 維度 | TO_DATE 寫法 | TO_CHAR 寫法 |
|---|---|---|
| 索引使用 | ✅ 可使用 `time` 欄位索引 (Range Scan) | ❌ 函數導致 Full Table Scan (除非建 FBI) |
| 邊界正確性 | ⚠️ 易寫錯,需記住「隔天 0 點」技巧 | ✅ 直觀,不易出錯 |
| 適用場景 | 大表、高頻查詢、需索引 | 小表、臨時查詢、報表、維護腳本 |
-- 既用索引又邊界正確
SELECT * FROM tablename
WHERE time >= TO_DATE('2011-05-02', 'YYYY-MM-DD')
AND time < TO_DATE('2011-05-31', 'YYYY-MM-DD'); -- 關鍵:用 < 隔天 0 點
若極少數情況需包含 23:59:59.999...,可用:
AND time < TO_DATE('2011-05-30', 'YYYY-MM-DD') + 1
CREATE INDEX idx_tablename_time_char ON tablename (TO_CHAR(time, 'YYYY-MM-DD'));
建立後,`TO_CHAR` 寫法即可走 Index Range Scan,兼顧直觀與效能。
日期區間查詢**核心原則**:以「左閉右開」區間 `[start, end+1day)` 為標準寫法。`TO_DATE` 配合 `< 隔天` 兼具索引與正確性;`TO_CHAR` 適合臨時分析,大表請搭配 FBI。切忌直接用 `<= TO_DATE('end_date')`,這是導致「少查一天」的最常見 Bug。