Oracle 中使用 SYSDATE 進行日期範圍查詢時,若寫法不當會導致索引失效、全表掃描。本文揭示動態值陷阱,提供預計算綁定變量優化重寫法,實測性能提升數十倍。
-- 慢查詢 (全表掃描,即使 eventdate 有索引)
SELECT * FROM eventtable WHERE eventdate > SYSDATE - 30;
根因:Oracle 優化器將 SYSDATE 視為**非確定性函數**,每行計算時重新求值,無法在解析階段確定常數邊界,導致無法生成索引範圍掃描計劃,退化為全表掃描 + 過濾。
-- 1. 應用層預計算 (Java 例)
String sql = "SELECT * FROM eventtable WHERE eventdate > :startDate";
Date startDate = new Date(System.currentTimeMillis() - 30L*24*3600*1000);
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDate(1, new java.sql.Date(startDate.getTime()));
ResultSet rs = ps.executeQuery();
-- 2. PL/SQL 綁定變量
VARIABLE v_start DATE;
EXEC :v_start := SYSDATE - 30;
SELECT * FROM eventtable WHERE eventdate > :v_start;
-- 3. 顯式 TO_DATE 字面量 (僅靜態 SQL)
SELECT * FROM eventtable WHERE eventdate > TO_DATE('2024-01-01', 'YYYY-MM-DD');
| 寫法 | 執行計劃 | 耗時 (百萬行表) | 邏輯讀 |
|---|---|---|---|
eventdate > SYSDATE - 30 | 全表掃描 (TABLE ACCESS FULL) | ~45 秒 | ~1,200,000 |
eventdate > :bind_var | 索引範圍掃描 (INDEX RANGE SCAN) | ~0.8 秒 | ~120 |
eventdate > TO_DATE(...) | 索引範圍掃描 | ~0.7 秒 | ~115 |
性能提升:50-60 倍,邏輯讀減少 4 個數量級。
SYSDATE、SYSTIMESTAMP、CURRENT_DATE、DBTIMEZONE — 每行求值,破壞 SARGTRUNC(eventdate) > ...、eventdate + 1 > ... — 同樣破壞索引VARIABLE / 綁定變量,避免直接嵌入 SYSDATEDBMS_STATS.GATHER_TABLE_STATS,保證優化器基數估算準確EXPLAIN PLAN 驗證關鍵查詢出現 INDEX RANGE SCAN 而非 FULLPARTITION RANGE)