核心原則:Oracle 解析 WHERE 子句遵循「自下而上」順序
雖然 CBO (Cost-Based Optimizer) 會根據統計資訊重新排序條件評估以降低成本,但理解解析器邏輯有助於編寫穩定、可讀、輔助優化的 SQL。
三大黃金法則
1. 表連接條件優先,強過濾條件後置
-- 低效:先評估簡單列過濾,最後才執行昂貴相關子查詢
SELECT ... FROM EMP E
WHERE SAL > 50000
AND JOB = 'MANAGER'
AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = E.EMPNO); -- 156.3 秒
-- 高效:先評估最耗資源/選擇性最強的條件 (相關子查詢),最後簡單過濾
SELECT ... FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = E.EMPNO)
AND SAL > 50000
AND JOB = 'MANAGER'; -- 10.6 秒
邏輯:解析器自下而上,最後寫的條件最先評估。將「大表掃描/複雜子查詢」放在最後 (最先跑),能盡早縮小結果集,後續簡單條件在小集合上評估極快。
2. 拒絕 `SELECT *`,明確列出欄位
- 解析開銷:`*` 需查詢 Data Dictionary (`USER_TAB_COLUMNS`) 展開所有欄位名,增加 Hard Parse 時間與 Library Cache 壓力。
- 網路傳輸:傳輸不必要欄位 (LOB、長字串、未用欄位) 浪費頻寬與客戶端記憶體。
- 應用脆弱性:`ALTER TABLE ADD COLUMN` 導致欄位順序/數量變更,依賴位置索引的程式 (Pro*C、JDBC `getString(1)`) 直接報錯或取錯值。
- 最佳實踐:`SELECT EMPNO, ENAME, SAL, DEPTNO FROM EMP`。
3. 善用表別名,消除欄位歧義
-- 不佳:欄位歧義 (DEPTNO 存在於 EMP 和 DEPT)
SELECT ENAME, DNAME, DEPTNO FROM EMP, DEPT WHERE EMP.DEPTNO = DEPT.DEPTNO;
-- 佳:別名 + 前綴
SELECT E.ENAME, D.DNAME, E.DEPTNO
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO;
- 減少解析器解析欄位歸屬的開銷。
- 避免 `ORA-00918: column ambiguously defined` 語法錯誤。
- 提升 SQL 可讀性與維護性 (特別是 3+ 表連接)。
進階補充:CBO 時代的條件排序現狀
- CBO 根據 **選擇性**、**索引可用性**、**連接方法** (Nested Loops / Hash Join / Sort Merge) 動態決定實際執行順序,`WHERE` 書寫順序**不直接決定**執行順序。
- 但:條件書寫順序影響解析樹結構,進而影響 CBO 搜尋空間與基數估算,間接影響最終計畫。
- 建議:邏輯分組書寫 — 連接條件 → 過濾條件 (高選擇性優先) → 其他,兼顧人類可讀與優化器友善。
快速檢查清單
- [ ] 所有欄位是否加了表別名前綴?
- [ ] 是否移除 `SELECT *` 明確列出業務需要欄位?
- [ ] 連接條件 (`ON`/`WHERE`) 是否集中在開頭?
- [ ] 最強過濾條件 (最小結果集) 是否放在 WHERE 末尾?
- [ ] 是否使用綁定變數而非字面值 (利用 Shared Cursor)?
- [ ] 是否避免了索引失效寫法 (函數、隱性轉換、負向條件、前導通配符)?
結語
掌握「連接優先、強過濾後置、拒絕星號、善用別名」四大原則,配合 CBO 統計資訊維護與索引設計,能寫出高效、穩定、易維護的 Oracle SQL。工具輔助:`EXPLAIN PLAN`、`AUTOTRACE`、`SQL Tuning Advisor` 驗證實際執行計畫永遠是最終標準。