首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle SQL 效能調優 8 大黃金法則:從 EXISTS 到 UNION 的實戰技巧

     編輯:Oracle 資料庫基礎

SQL 效能調優無捷徑,但掌握核心原則即能解決 80% 的效能問題。本文整理 8 大經典調優法則,附反面教材與正確寫法對照,助您寫出高效 SQL。

1. 用 EXISTS 替代 IN,NOT EXISTS 替代 NOT IN

原則:子查詢若只需判斷「存在與否」,EXISTS 只需掃描到第一筆符合即返回,IN 需將子查詢結果全數物化再比對。

-- 低效:IN 需完整掃描 DEPT
SELECT * FROM EMP WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC = 'MELB');

-- 高效:EXISTS 遇第一筆即停止
SELECT * FROM EMP e WHERE EXISTS (SELECT 'X' FROM DEPT d WHERE d.DEPTNO = e.DEPTNO AND d.LOC = 'MELB');

2. 用 WHERE 替代 HAVING 過濾分組前資料

原則:HAVING 在 GROUP BY 完成後才過濾,需先聚合全量資料;WHERE 可在聚合前過濾,減少參與排序/聚合的資料量。

-- 低效:先 GROUP BY 全表再 HAVING
SELECT REGION, AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION HAVING REGION NOT IN ('SYDNEY','PERTH');

-- 高效:WHERE 先過濾再 GROUP BY
SELECT REGION, AVG(LOG_SIZE) FROM LOCATION WHERE REGION NOT IN ('SYDNEY','PERTH') GROUP BY REGION;

3. 善用表別名,前綴所有欄位

原則:多表 JOIN 時加上別名並前綴欄位(e.EMPNO),減少解析器解析歧義的時間,也能避免同名欄位報錯。

4. 用 EXISTS 替代 DISTINCT 去重

原則:一對多關聯查詢時,DISTINCT 需排序去重;EXISTS 只需確認存在即可,避免排序開銷。

-- 低效:DISTINCT 需排序
SELECT DISTINCT d.DEPT_NO, d.DEPT_NAME FROM DEPT d, EMP e WHERE d.DEPT_NO = e.DEPT_NO;

-- 高效:EXISTS 無排序
SELECT d.DEPT_NO, d.DEPT_NAME FROM DEPT d WHERE EXISTS (SELECT 'X' FROM EMP e WHERE e.DEPT_NO = d.DEPT_NO);

5. 索引欄位用 UNION 替代 OR

原則:OR 導致全表掃描;UNION 可分別走各索引再合併(前提:所有欄位皆有索引)。

-- 高效:UNION 走兩個索引
SELECT LOC_ID, LOC_DESC, REGION FROM LOCATION WHERE LOC_ID = 10
UNION
SELECT LOC_ID, LOC_DESC, REGION FROM LOCATION WHERE REGION = 'MELBOURNE';

-- 低效:OR 導致全表掃描
SELECT LOC_ID, LOC_DESC, REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = 'MELBOURNE';
若堅持用 OR,將選擇率最高(回傳行數最少)的索引欄位寫在最前面。

6. 用 IN 替代多個 OR(同一欄位多值)

-- 低效
SELECT ... FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30;

-- 高效:IN 內部優化為排序後二分搜尋
SELECT ... FROM LOCATION WHERE LOC_ID IN (10, 20, 30);

7. WHERE 子句條件順序:連接條件前、高過濾率條件後

原則:Oracle 解析器採「自下而上」順序評估 WHERE 條件(舊版 RBO 模式),將表連接條件寫在前面,高選擇性過濾條件寫在最後。

-- 低效(156.3 秒):高過濾條件寫在前
SELECT ... FROM EMP e WHERE SAL > 50000 AND JOB = 'MANAGER' AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = e.EMPNO);

-- 高效(10.6 秒):子查詢高過濾條件前置
SELECT ... FROM EMP e WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = e.EMPNO) AND SAL > 50000 AND JOB = 'MANAGER';

8. 計算筆數:COUNT(*) vs COUNT(1) vs COUNT(索引欄位)

結論:COUNT(*) ≈ COUNT(1)(現代版本已優化相同),但 COUNT(索引欄位) 若該索引為 NOT NULL 且較窄,可走 Index Fast Full Scan 最快。

-- 最快:走 EMPNO 索引
SELECT COUNT(EMPNO) FROM EMP;

總結檢查清單

  • ☐ 子查詢存在性判斷用 EXISTS
  • ☐ 分組前過濾用 WHERE 非 HAVING
  • ☐ 多表 JOIN 全程加別名
  • ☐ 去重改用 EXISTS
  • ☐ 索引欄位 OR 改 UNION
  • ☐ 同欄位多值 OR 改 IN
  • ☐ WHERE 條件順序優化
  • ☐ 統計筆數優先用索引欄位
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved