Oracle 優化器在 WHERE 條件對列使用函數 (UPPER、REPLACE、SUBSTR、TRUNC 等) 時,會導致該列索引失效,退化為全表掃描。本文揭示「增加額外篩選條件強制索引範圍掃描」的核心技巧,並給出大小寫混合、電話號碼格式化、日期截斷三大典型場景完整解決方案。
索引存儲的是列的原始值 (如 'john')。查詢 WHERE UPPER(name) = 'JOHN' 時,優化器無法將 'JOHN' 與索引中的 'john' 直接比較,因為函數改變了比較值的形態。優化器只能全表掃描,對每行計算 UPPER(name) 再比較。
在 WHERE 子句中 AND 額外條件,該條件:
-- 原查詢:全表掃描
SELECT address FROM address WHERE UPPER(name) LIKE 'JOHN';
-- 強制索引:添加首字母大小寫範圍
SELECT address FROM address
WHERE UPPER(name) LIKE 'JOHN'
AND (name LIKE 'J%' OR name LIKE 'j%');
-- 進一步細化:前兩字母組合
SELECT address FROM address
WHERE UPPER(name) LIKE 'JOHN'
AND (name LIKE 'JO%' OR name LIKE 'Jo%' OR name LIKE 'jO%' OR name LIKE 'jo%');
執行計畫變化:
全表掃描 → CONCATENATION → 多個 INDEX RANGE SCAN → TABLE ACCESS BY INDEX ROWID。
每個 LIKE 條件觸發一次索引範圍掃描,結果合併。
數據格式多樣:123-456-7890, 123 456 7890, (123) 456-7890。
-- 原查詢:雙重 REPLACE 導致索引失效
WHERE REPLACE(REPLACE(phone_number, '-', ''), ' ', '') = '1234567890'
-- 強制索引:添加前綴匹配
WHERE REPLACE(REPLACE(phone_number, '-', ''), ' ', '') = '1234567890'
AND phone_number LIKE '123%'
-- 含括號情況:擴展前綴選項
WHERE REPLACE(REPLACE(REPLACE(REPLACE(phone_number, '-', ''), ' ', ''), '(', ''), ')', '') = '1234567890'
AND (phone_number LIKE '123%' OR phone_number LIKE '(123%')
-- 查詢某天的訂單
WHERE TRUNC(order_date) = DATE '2024-01-15'
-- 強制索引:添加日期範圍
WHERE TRUNC(order_date) = DATE '2024-01-15'
AND order_date >= DATE '2024-01-15'
AND order_date < DATE '2024-01-16' -- 開區間避免時分秒問題
原則:日期範圍條件 (>= <) 可直接使用索引,且不改變結果。
若頻繁對函數結果查詢,可直接在函數表達式上建索引:
CREATE INDEX idx_upper_name ON address (UPPER(name));
CREATE INDEX idx_trunc_order_date ON orders (TRUNC(order_date));
CREATE INDEX idx_clean_phone ON customers (REPLACE(REPLACE(phone, '-', ''), ' ', ''));
-- 查詢自動使用函數索引
SELECT * FROM address WHERE UPPER(name) = 'JOHN';
優勢:無需修改 SQL、索引維護由資料庫自動、支援複雜表達式。
劣勢:插入/更新開銷增加、統計信息需定期收集、佔用額外空間。
LIKE '%') 無效;過窄可能漏數據 (邏輯錯誤)。Oracle WHERE 條件含函數導致索引失效,核心破解法是「AND 原始列範圍條件」。三大場景模板:
1. 大小寫 → LIKE 'J%' OR LIKE 'j%'
2. 格式化 → LIKE '純數字前綴%'
3. 日期截斷 → >= 當天 00:00 AND < 明天 00:00
長期治理建議:入庫標準化 + 函數索引/虛擬列,從源頭消除函數查詢需求。