Oracle SQL 優化的核心在於讓優化器選擇索引存取路徑、避免全表掃描、減少排序/雜湊開銷。本文針對常見低效寫法提供具體改寫模式與原理說明。
現象:WHERE col IN (subquery) 可能轉換為半連結,但含分組/聚合的子查詢無法轉換,導致先執行子查詢再外層掃描。
建議:業務核心 SQL 盡量避免 IN,改用 EXISTS 或 JOIN。
-- 低效
SELECT * FROM Orders WHERE CustomerID IN (SELECT CustomerID FROM Customers WHERE Region = 'West');
-- 高效:EXISTS 相關子查詢
SELECT * FROM Orders o WHERE EXISTS (SELECT 1 FROM Customers c WHERE c.CustomerID = o.CustomerID AND c.Region = 'West');
-- 高效:JOIN (需去重)
SELECT DISTINCT o.* FROM Orders o JOIN Customers c ON o.CustomerID = c.CustomerID WHERE c.Region = 'West';
原因:NOT IN 遇到子查詢回傳 NULL 時,整個條件永遠為 UNKNOWN,導致結果為空;且無法使用索引。
替代方案:
-- 低效
SELECT * FROM Products WHERE ProductID NOT IN (SELECT ProductID FROM Discontinued);
-- 高效:NOT EXISTS
SELECT * FROM Products p WHERE NOT EXISTS (SELECT 1 FROM Discontinued d WHERE d.ProductID = p.ProductID);
-- 高效:LEFT JOIN + IS NULL
SELECT p.* FROM Products p LEFT JOIN Discontinued d ON p.ProductID = d.ProductID WHERE d.ProductID IS NULL;
特性:永遠不使用索引,導致全表掃描。
改寫 (需確認資料分布):
WHERE status <> 0 -- 改為
WHERE status > 0 OR status < 0
WHERE code <> '' -- 改為
WHERE code > '' -- 若 code 為非負字串編碼
特性:B-tree 索引不儲存 NULL,故 IS [NOT] NULL 通常全表掃描。
改寫:
WHERE col IS NOT NULL -- 改為
WHERE col > 0 -- 數值型
WHERE col > '' -- 字串型
-- 或:不允許 NULL,給預設值
ALTER TABLE T MODIFY col DEFAULT 0 NOT NULL;
替代索引**:位圖索引 可索引 NULL,但不適用高併發 OLTP (鎖競爭劇烈),僅適合資料倉儲/低更新表。
同一欄位、同一常數,>= 比 > 可能更優:
-- 假設 A 欄位值分布:30萬=0, 30萬=1, 39萬=2, 1萬=3
WHERE A > 2 -- 優化器先找 =2 的索引項再比較
WHERE A >= 3 -- 直接定位 =3 的索引項,更快
前置萬用字元無法用索引:LIKE '%5400%' → 全表掃描。
改寫:若知道前綴模式,拆解為多個前綴比對:
-- 低效
WHERE YY_BH LIKE '%5400%'
-- 高效:已知前綴為 'X' 或 'B'
WHERE YY_BH LIKE 'X5400%' OR YY_BH LIKE 'B5400%' -- 兩次索引範圍掃描
UNION**:合併後排序去重,大量資料可能落磁碟排序。
UNION ALL**:直接合併,**無排序去重**,效能極高。
建議:業務確定無重複或允許重複時,**永遠用 UNION ALL**。
SELECT * FROM CurrentMonthSales
UNION ALL
SELECT * FROM HistorySales;
同樣功能,不同寫法可能產生不同計畫。建議:
| 低效寫法 | 推薦改寫 | 核心理由 |
|---|---|---|
IN (subquery) | EXISTS / JOIN | 避免子查詢具體化、利用索引巢狀迴圈 |
NOT IN | NOT EXISTS / LEFT JOIN ... IS NULL | NULL 語意陷阱、無索引 |
col <> val | col > val OR col < val | <> 永遠全掃 |
IS [NOT] NULL | 預設值 / 位圖索引 (僅 DW) | B-tree 不存 NULL |
LIKE '%abc%' | LIKE 'abc%' / 全文檢索 | 前置萬用字元殺索引 |
UNION | UNION ALL | 去重排序開銷 |
將上述模式內化為編碼肌肉記憶,配合定期 EXPLAIN PLAN / SQL Monitoring 驗證,即能持續產出高效能 Oracle SQL。