WHERE 子句優化
- 避免子查詢:能用 `JOIN`/`EXISTS` 解決的不用子查詢。
- 日期查詢不轉型:
❌ `WHERE TO_CHAR(dt, 'YYYY-MM-DD') = '2023-01-01'`
✅ `WHERE dt >= '2023-01-01' AND dt < '2023-01-02'` (SARGable、用索引)
- 高選擇率條件後置:Oracle 解析順序自右至左 (舊版 RBO) / 現代 CBO 自動優化,但**寫法上將最過濾的條件放最後**是好習慣。
- 使用綁定變數:`WHERE id = :b1` 而非拼接字串,共用執行計畫、防注入。
- 避免 OR:`OR` 往往導致全表掃描 → 改 `IN` / `UNION ALL` / 多條件索引。
- EXISTS 替代 IN:`EXISTS` 半連接、遇真即停;`NOT EXISTS` 優於 `NOT IN` (NULL 三值邏輯陷阱)。
- 索引欄不運算:❌ `WHERE sal*12 > 25000` → ✅ `WHERE sal > 25000/12`。
- IN 替代多個 OR:`WHERE dept IN (10,20,30)` 可用索引範圍掃描。
- 避免 IS NULL/IS NOT NULL:B-tree 索引不存全 NULL 鍵,必走全掃。
- 必用索引首欄:複合索引 `(a,b,c)`,查詢必含 `a` 才能用索引。
- UNION ALL 替代 UNION:`UNION` 需排序去重,`UNION ALL` 直接合併,效能差數倍。
- 避免隱含轉換:❌ `WHERE varchar_col = 123` → ✅ `WHERE varchar_col = '123'`,否則索引失效。
- `!=` / `<>` 不走索引:改寫為範圍或 `IN` 列舉正向值。
- 優化 GROUP BY:先過濾再分組、利用索引順序避免排序。
- LIKE 通配符前置不走索引:`LIKE '%abc'` 全掃;`LIKE 'abc%'` 走索引。
- 避免複雜正則:`LIKE '98___'` 全掃 → 改 `WHERE zip BETWEEN '98000' AND '98999'` 走索引。
- 明確 SELECT 清單:❌ `SELECT *` → ✅ `SELECT col1, col2`,減少 I/O、網路、鎖競爭、利用覆蓋索引。
- 表別名 + 欄位前綴:`SELECT o.id FROM orders o`,避免歧義、提升解析速度。
- 遵循資料庫習慣寫法:大寫關鍵字、縮排、註釋,利於維護與計畫快取命中。
進階補充
- **統計資訊是優化器的眼睛**:定期 `UPDATE STATISTICS` / `DBMS_STATS.GATHER_TABLE_STATS`。
- **執行計畫是真相**:學會讀 `EXPLAIN PLAN` / `SHOWPLAN_XML`,關注 Cardinality Estimation、Access Path、Join Method。
- **索引非越多越好**:OLTP 表 5-6 支上限,寫入每多一索引就多一筆維護開銷。
- **分頁查詢**:舊版 `ROWNUM` / 新版 `OFFSET FETCH` / Keyset Pagination (游標分頁) 視版本選擇。
結語
這 20 條法則涵蓋 **SARGable 寫法、索引匹配、連接優化、統計維護、計畫穩定** 核心維度。新手先背誦、再實戰、再理解原理,養成「寫 SQL 先想索引」的肌肉記憶,效能調優就已成功一半。