Oracle+SQL優化第四彈

分類:編程 時間:2017-03-30

Oracle+SQL優化第四彈

21 用 EXISTS 替換 DISTINCT

當提交一個包含一對多表信息(比如部門表和雇員表)的查詢時,避免在 SELECT 子句中使用

DISTINCT. 一般可以考慮用 EXIST 替換

例如:

低效:

SELECT DISTINCT DEPT_NO,DEPT_NAME

FROM DEPT D,EMP E

WHERE D.DEPT_NO = E.DEPT_NO

高效:

SELECT DEPT_NO,DEPT_NAME

FROM DEPT D

WHERE EXISTS ( SELECT ‘X’

FROM EMP E

WHERE E.DEPT_NO = D.DEPT_NO);

EXISTS 使查詢更為迅速,因為 RDBMS 核心模塊將在子查詢的條件一旦滿足後,立刻返回結

果.

22 識別’低效執行’的 SQL 語句

用下列 SQL 工具找出低效 SQL:

SELECT EXECUTIONS , DISK_READS, BUFFER_GETS,

ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,

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;

(譯者按: 雖然目前各種關於 SQL 優化的圖形化工具層出不窮,但是寫出自己的 SQL 工具來解決問題始終是一個最好的方法)

23 使用 TKPROF 工具來查詢 SQL 性能狀態

SQL trace 工具收集正在執行的 SQL 的性能狀態數據並記錄到一個跟蹤文件中. 這個跟蹤文

件提供了許多有用的信息,例如解析次數.執行次數,CPU 使用時間等.這些數據將可以用來優化你

的系統.

設置 SQL TRACE 在會話級別: 有效

ALTER SESSION SET SQL_TRACE TRUE

設置 SQL TRACE 在整個數據庫有效仿, 你必須將 SQL_TRACE 參數在 init.ora 中設為 TRUE, USER_DUMP_DEST 參數說明了生成跟蹤文件的目錄

(譯者按: 這一節中,作者並沒有提到 TKPROF 的用法, 對 SQL TRACE 的用法也不夠準確, 設

置 SQL TRACE 首先要在 init.ora 中設定 TIMED_STATISTICS, 這樣才能得到那些重要的時間狀態. 生成的 trace 文件是不可讀的,所以要用 TKPROF 工具對其進行轉換,TKPROF 有許多執行參數.

大家可以參考 ORACLE 手冊來了解具體的配置. )

24 用 EXPLAIN PLAN 分析 SQL 語句

EXPLAIN PLAN 是一個很好的分析 SQL 語句的工具,它甚至可以在不執行 SQL 的情況下分

析語句. 通過分析,我們就可以知道 ORACLE 是怎麽樣連接表,使用什麽方式掃描表(索引掃描或全

表掃描)以及使用到的索引名稱.

你需要按照從裏到外,從上到下的次序解讀分析的結果. EXPLAIN PLAN 分析的結果是用縮

進的格式排列的, 最內部的操作將被最先解讀, 如果兩個操作處於同一層中,帶有最小操作號的將

被首先執行.

NESTED LOOP 是少數不按照上述規則處理的操作, 正確的執行路徑是檢查對 NESTED

LOOP 提供數據的操作,其中操作號最小的將被最先處理.

譯者按:

通過實踐, 感到還是用 SQLPLUS 中的 SET TRACE 功能比較方便.

舉例:

SQL> list

1SELECT *

2FROM dept, emp

3* WHERE emp.deptno = dept.deptno

SQL> set autotrace traceonly /*traceonly 可以不顯示執行結果*/ SQL> /

14 rows selected. Execution Plan

----------------------------------------------------------

0SELECT STATEMENT Optimizer=CHOOSE

10 NESTED LOOPS

21 TABLE ACCESS (FULL) OF 'EMP'

31 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

43 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

Statistics

----------------------------------------------------------

0 recursive calls

2 db block gets

30 consistent gets

0 physical reads

0 redo size

2598 bytes sent via SQL*Net to client

503 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

14 rows processed

通過以上分析,可以得出實際的執行步驟是:

1.TABLE ACCESS (FULL) OF 'EMP'

2.INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

4.NESTED LOOPS (JOINING 1 AND 3)

註: 目前許多第三方的工具如 TOAD 和 ORACLE 本身提供的工具如 OMS 的 SQL Analyze 都

提供了極其方便的 EXPLAIN PLAN 工具.也許喜歡圖形化界面的朋友們可以選用它們.

25 用索引提高效率

索引是表的一個概念部分,用來提高檢索數據的效率. 實際上,ORACLE 使用了一個復雜的自

平衡 B-tree 結構. 通常,通過索引查詢數據比全表掃描要快. 當 ORACLE 找出執行查詢和 update 語

句的最佳路徑時, ORACLE 優化器將使用索引. 同樣在聯結多個表時使用索引也可以提高效率. 另

一個使用索引的好處是,它提供了主鍵(primary key)的唯一性驗證.

除了那些 LONG 或 LONG RAW 數據類型, 你可以索引幾乎所有的列. 通常, 在大型表中使用

索引特別有效. 當然,你也會發現, 在掃描小表時,使用索引同樣能提高效率.

雖然使用索引能得到查詢效率的提高,但是我們也必須註意到它的代價. 索引需要空間來存儲,

也需要定期維護, 每當有記錄在表中增減或索引列被修改時, 索引本身也會被修改. 這意味著每條

記錄的 INSERT , DELETE , UPDATE 將為此多付出 4 , 5 次的磁盤 I/O . 因為索引需要額外的存儲

空間和處理,那些不必要的索引反而會使查詢反應時間變慢.

譯者按:

定期的重構索引是有必要的.

ALTER INDEX REBUILD

26 索引的操作

ORACLE 對索引有兩種訪問模式. 1、索引唯一掃描 ( INDEX UNIQUE SCAN)

大多數情況下, 優化器通過 WHERE 子句訪問 INDEX.

例如:

表 LODGING 有兩個索引 : 建立在 LODGING 列上的唯一性索引 LODGING_PK 和建立在

MANAGER 列上的非唯一性索引 LODGING$MANAGER.

SELECT *

FROM LODGING

WHERE LODGING = ‘ROSE HILL’;

在內部 , 上述 SQL 將被分成兩步執行, 首先 , LODGING_PK 索引將通過索引唯一掃描的方式被訪問 , 獲得相對應的 ROWID, 通過 ROWID 訪問表的方式 執行下一步檢索.

如果被檢索返回的列包括在 INDEX 列中,ORACLE 將不執行第二步的處理(通過 ROWID 訪

問表). 因為檢索數據保存在索引中, 單單訪問索引就可以完全滿足查詢結果.

下面 SQL 只需要 INDEX UNIQUE SCAN 操作.

SELECT LODGING

FROM LODGING

WHERE LODGING = ‘ROSE HILL’;

索引範圍查詢(INDEX RANGE SCAN)

適用於兩種情況:

1.基於一個範圍的檢索

2.基於非唯一性索引的檢索

例1:

SELECT LODGING

FROM LODGING

WHERE LODGING LIKE ‘M%’;

WHERE 子句條件包括一系列值, ORACLE 將通過索引範圍查詢的方式查詢 LODGING_PK .

由於索引範圍查詢將返回一組值, 它的效率就要比索引唯一掃描低一些.

例 2:

SELECT LODGING

FROM LODGING

WHERE MANAGER = ‘BILL GATES’;

這個 SQL 的執行分兩步, LODGING$MANAGER 的索引範圍查詢(得到所有符合條件記錄的

ROWID) 和下一步同過 ROWID 訪問表得到 LODGING 列的值. 由於 LODGING$MANAGER 是一個非唯一性的索引,數據庫不能對它執行索引唯一掃描.

由於 SQL 返回 LODGING 列,而它並不存在於 LODGING$MANAGER 索引中, 所以在索引

範圍查詢後會執行一個通過 ROWID 訪問表的操作.

WHERE 子句中, 如果索引列所對應的值的第一個字符由通配符(WILDCARD)開始, 索引將不被采用.

SELECT LODGING

FROM LODGING

WHERE MANAGER LIKE ‘%HANMAN’;

在這種情況下,ORACLE 將使用全表掃描.

27 基礎表的選擇

基礎表(Driving Table)是指被最先訪問的表(通常以全表掃描的方式被訪問). 根據優化器的不同, SQL 語句中基礎表的選擇是不一樣的.

如果你使用的是 CBO (COST BASED OPTIMIZER),優化器會檢查 SQL 語句中的每個表的物理大小,索引的狀態,然後選用花費最低的執行路徑.

如果你用 RBO (RULE BASED OPTIMIZER) , 並且所有的連接條件都有索引對應, 在這種情況下, 基礎表就是 FROM 子句中列在最後的那個表.

舉例:

SELECT A.NAME , B.MANAGER

FROM WORKER A,

LODGING B

WHERE A.LODGING = B.LODING;

由於 LODGING 表的 LODING 列上有一個索引, 而且 WORKER 表中沒有相比較的索引, WORKER 表將被作為查詢中的基礎表.

28 多個平等的索引

當 SQL 語句的執行路徑可以使用分布在多個表上的多個索引時, ORACLE 會同時使用多個

索引並在運行時對它們的記錄進行合並, 檢索出僅對全部索引有效的記錄.

在 ORACLE 選擇執行路徑時,唯一性索引的等級高於非唯一性索引. 然而這個規則只有

當 WHERE 子句中索引列和常量比較才有效.如果索引列和其他表的索引類相比較. 這種子句

在優化器中的等級是非常低的.

如果不同表中兩個想同等級的索引將被引用, FROM 子句中表的順序將決定哪個會被率先使用. FROM 子句中最後的表的索引將有最高的優先級.

如果相同表中兩個想同等級的索引將被引用, WHERE 子句中最先被引用的索引將有最高的

優先級.

舉例:

DEPTNO 上有一個非唯一性索引,EMP_CAT 也有一個非唯一性索引.

SELECT ENAME,

FROM EMP

WHERE DEPT_NO = 20

AND EMP_CAT = ‘A’;

這裏,DEPTNO 索引將被最先檢索,然後同 EMP_CAT 索引檢索出的記錄進行合並. 執行路徑

如下:

TABLE ACCESS BY ROWID ON EMP

AND-EQUAL

INDEX RANGE SCAN ON DEPT_IDX

INDEX RANGE SCAN ON CAT_IDX

29 等式比較和範圍比較

當 WHERE 子句中有索引列, ORACLE 不能合並它們,ORACLE 將用範圍比較.

舉例:

DEPTNO 上有一個非唯一性索引,EMP_CAT 也有一個非唯一性索引.

SELECT ENAME

FROM EMP

WHERE DEPTNO > 20

AND EMP_CAT = ‘A’;

這裏只有 EMP_CAT 索引被用到,然後所有的記錄將逐條與 DEPTNO 條件進行比較. 執行路

徑如下:

TABLE ACCESS BY ROWID ON EMP

INDEX RANGE SCAN ON CAT_IDX

30 不明確的索引等級

當 ORACLE 無法判斷索引的等級高低差別,優化器將只使用一個索引,它就是在 WHERE 子句

中被列在最前面的.

舉例:

DEPTNO 上有一個非唯一性索引,EMP_CAT 也有一個非唯一性索引.

SELECT ENAME

FROM EMP

WHERE DEPTNO > 20

AND EMP_CAT > ‘A’;

這裏, ORACLE 只用到了 DEPT_NO 索引. 執行路徑如下:

TABLE ACCESS BY ROWID ON EMP

INDEX RANGE SCAN ON DEPT_IDX

譯者按:

我們來試一下以下這種情況:

SQL> select index_name, uniqueness from user_indexes where table_name = 'EMP';

INDEX_NAME UNIQUENES

------------------------------ ---------

EMPNO UNIQUE

EMPTYPE NONUNIQUE

SQL> select * from emp where empno >= 2 and emp_type = 'A' ; no rows selected

Execution Plan

----------------------------------------------------------

0SELECT STATEMENT Optimizer=CHOOSE

10 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'

21 INDEX (RANGE SCAN) OF 'EMPTYPE' (NON-UNIQUE)

雖然 EMPNO 是唯一性索引,但是由於它所做的是範圍比較, 等級要比非唯一性索引的等式比

較低!

31 強制索引失效

如果兩個或以上索引具有相同的等級,你可以強制命令 ORACLE 優化器使用其中的一個(通

過它,檢索出的記錄數量少) .

舉例:

SELECT ENAME

FROM EMP

WHERE EMPNO = 7935

AND DEPTNO + 0 = 10 /*DEPTNO 上的索引將失效*/

AND EMP_TYPE || ‘’ = ‘A’ /*EMP_TYPE 上的索引將失效*/

這是一種相當直接的提高查詢效率的辦法. 但是你必須謹慎考慮這種策略,一般來說,只有在你

希望單獨優化幾個 SQL 時才能采用它.

這裏有一個例子關於何時采用這種策略, 假設在 EMP 表的 EMP_TYPE 列上有一個非唯一性

的索引而 EMP_CLASS 上沒有索引.

SELECT ENAME

FROM EMP

WHERE EMP_TYPE = ‘A’

AND EMP_CLASS = ‘X’;

優化器會註意到 EMP_TYPE 上的索引並使用它. 這是目前唯一的選擇. 如果,一段時間以後,

另一個非唯一性建立在 EMP_CLASS 上,優化器必須對兩個索引進行選擇,在通常情況下,優化器將

使用兩個索引並在他們的結果集合上執行排序及合並. 然而,如果其中一個索引(EMP_TYPE)接

近於唯一性而另一個索引(EMP_CLASS)上有幾千個重復的值. 排序及合並就會成為一種不必

要的負擔. 在這種情況下,你希望使優化器屏蔽掉 EMP_CLASS 索引.

用下面的方案就可以解決問題.

SELECT ENAME

FROM EMP

WHERE EMP_TYPE = ‘A’

AND EMP_CLASS||’’ = ‘X’;

32 避免在索引列上使用計算

WHERE 子句中,如果索引列是函數的一部分.優化器將不使用索引而使用全表掃描.

舉例:

低效:

SELECT …

FROM DEPT

WHERE SAL * 12 > 25000;

高效:

SELECT …

FROM DEPT

WHERE SAL > 25000/12;

譯者按:

這是一個非常實用的規則,請務必牢記

33 自動選擇索引

如果表中有兩個以上(包括兩個)索引,其中有一個唯一性索引,而其他是非唯一性.

在這種情況下,ORACLE 將使用唯一性索引而完全忽略非唯一性索引.

舉例:

SELECT ENAME

FROM EMP

WHERE EMPNO = 2326

AND DEPTNO = 20 ;

這裏,只有 EMPNO 上的索引是唯一性的,所以 EMPNO 索引將用來檢索記錄.

TABLE ACCESS BY ROWID ON EMP

INDEX UNIQUE SCAN ON EMP_NO_IDX

34 避免在索引列上使用 NOT

通常,我們要避免在索引列上使用 NOT, NOT 會產生在和在索引列上使用函數相同的影響.

當 ORACLE”遇到”NOT,他就會停止使用索引轉而執行全表掃描.

舉例:

低效: (這裏,不使用索引)

SELECT …

FROM DEPT

WHERE DEPT_CODE NOT = 0;

高效: (這裏,使用了索引)

SELECT …

FROM DEPT

WHERE DEPT_CODE > 0;

需要註意的是,在某些時候, ORACLE 優化器會自動將 NOT 轉化成相對應的關系操作符. NOT > to <=

NOT >= to < NOT < to >= NOT <= to >

譯者按:

在這個例子中,作者犯了一些錯誤. 例子中的低效率 SQL 是不能被執行的。
Tags:

文章來源:


ads
ads

相關文章
ads

相關文章

ad