查詢結構優化
- 表順序(RBO):FROM 右至左解析,記錄最少的表放最後(做驅動表)。三表以上找交叉表。
- WHERE 條件順序:自下而上解析,表連接寫最前,過濾性最強條件寫最後。
- 避免 SELECT *:轉換欄位名需查資料字典,增解析開銷。
- 減少資料庫往返:批次處理、陣列提取(
ARRAYSIZE 200)。
- DECODE/CASE 避免重複掃描:單次掃描完成多條件分支。
- 合併無關查詢:多條簡單查詢合為一條,減少解析與網路開銷。
DML 優化
- 刪除重複用 ROWID:
DELETE FROM emp e
WHERE e.rowid > (SELECT MIN(x.rowid) FROM emp x WHERE x.emp_no = e.emp_no);
- TRUNCATE 替代 DELETE:不產生 UNDO、不觸發 Trigger、高水位歸零(DDL,不可回滾)。
- 頻繁 COMMIT:釋放 UNDO、鎖、Redo Log Buffer、管理開銷。
條件改寫
- WHERE 替代 HAVING:HAVING 在分組後過濾,WHERE 在分組前過濾,減少排序聚合開銷。
- 減少子查詢掃表:改用 JOIN 或 WITH 子句。
- EXISTS 替代 IN、NOT EXISTS 替代 NOT IN:後者觸發子查詢全表掃描+排序合併。
- EXISTS 替代 DISTINCT:一對多查詢去重,EXISTS 滿足即返回,避免排序。
索引使用鐵律
- 別名 + 前綴欄位:減少解析時間、避免歧義。
- 避免索引列上用 NOT、函數、運算:均導致全表掃描。
- ❌
sal * 12 > 25000 → ✅ sal > 25000/12
- ❌
WHERE dept_code IS NOT NULL → ✅ WHERE dept_code >= 0
- >= 替代 >:直接定位首滿足行,避免掃描邊界值。
- UNION 替代 OR(索引列):OR 導致全掃,UNION 可分別走索引再合併。
- IN 替代 多個 OR:可讀性與效能兼顧(8i 後優化器對等)。
- 複合索引必須引用導欄:否則索引失效。
- UNION ALL 替代 UNION:免排序,但需業務允許重複。
- WHERE 替代 ORDER BY:ORDER BY 需嚴格符合索引順序且非空才走索引。
- 隱式類型轉換殺索引:
char_col = 123 會轉 TO_NUMBER(char_col) 導致失效,寫 char_col = '123'。
殺手級 WHERE 陷阱
統計與存取路徑
- 存取超過 30% 資料 → 全表掃描更優。
- 索引未必比全掃快(同數量級),但通常快數倍至數千倍。
- DISTINCT/UNION/MINUS/INTERSECT/ORDER BY 觸發 SORT,調大
SORT_AREA_SIZE。
- GROUP BY 前先 WHERE 過濾,大幅減少分組資料量。
自動發現低效 SQL
SELECT executions, disk_reads, buffer_gets,
ROUND((buffer_gets-disk_reads)/buffer_gets,2) hit_ratio,
ROUND(disk_reads/executions,2) reads_per_run,
sql_text
FROM v$sqlarea
WHERE executions > 0
AND buffer_gets > 0
AND (buffer_gets-disk_reads)/buffer_gets < 0.8
ORDER BY 4 DESC;
索引維護
定期 ALTER INDEX idx REBUILD [TABLESPACE ts] 消除碎片、恢復聚簇因子。