Oracle 語句優化

1.        ORACLE 的解析器按照從右到左的順序處理 FROM 子句中的表名,因此 FROM 子句中寫在最後的表(基礎表  driving table)將被最先處理。在 FROM 子句中包含多個表的情況下,你必須選擇記錄條數最少的表作爲基礎表。

例如:

              TAB1 16384 條記錄

             TAB2 1 條記錄

           選擇 TAB2作爲基礎表  (最好的方法)

           select count(*) from tab1tab2 執行時間 0.96

           選擇 TAB2作爲基礎表  (不佳的方法)

           select count(*) from tab2tab1 執行時間 26.09

2.        ORACLE 採用自下而上的順序解析 WHERE 子句,根據這個原理,表之間的連接必須寫在其他 WHERE 條件之前, 那些可以過濾掉最大數量記錄的條件必須寫在 WHERE 子句的尾。

例如:

    (低效,執行時間 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’;

3.        SELECT子句中避免使用’*’

當你想在 SELECT子句中列出所有的 COLUMN時,使用動態 SQL列引用  ‘*’ 是一個方便的方法。不幸的是,這是一個非常低效的方法。 實際上,ORACLE 在解析的過程中,會將‘*’ 依次轉換成所有的列名, 這個工作是通過查詢數據字典完成的, 這意味着將耗費更多的時間。

4.        使用DECODE 函數來減少處理時間, 使用 DECODE 函數可以避免重複掃描相同記錄或重複連接相同的表。

5.        刪除重複記錄

最高效的刪除重複記錄方法  ( 因爲使用了 ROWID)

DELETE FROM EMP E

WHERE E.ROWID > (SELECT MIN(X.ROWID)  

                         FROM EMP X

                         WHERE X.EMP_NO = E.EMP_NO);

6.        計算記錄條數

和一般的觀點相反,count(*) count(1)稍快,當然如果可以通過索引檢索,對索

引列的計數仍舊是最快的。 例如 COUNT(EMPNO)

7.        WHERE子句替換HAVING子句

避免使用 HAVING子句,HAVING 只會在檢索出所有記錄之後纔對結果集進行過濾。 

這個處理需要排序,總計等操作。 如果能通過 WHERE 子句限制記錄的數目,那就能減少這方面的開銷。

例如:

低效

SELECT REGIONAVG(LOG_SIZE)  

   FROM LOCATION  

   GROUP BY REGION  

   HAVING REGION REGION != ‘SYDNEY’

   AND REGION != ‘PERTH’

高效

SELECT REGIONAVG(LOG_SIZE)  

FROM LOCATION  

WHERE REGION REGION != ‘SYDNEY’  

AND REGION != ‘PERTH’  

GROUP BY REGION

HAVING 中的條件一般用於對一些集合函數的比較,如 COUNT() 等等。除此而外,一般的條件應該寫在 WHERE 子句中。

8.        減少對錶的查詢

在含有子查詢的 SQL語句中,要特別注意減少對錶的查詢。

例如:

低效

    SELECT TAB_NAME  

    FROM TABLES  

    WHERE TAB_NAME = ( SELECT TAB_NAME  

    FROM TAB_COLUMNS  

    WHERE VERSION = 604)  

    AND  DB_VER= ( SELECT DB_VER  

    FROM TAB_COLUMNS  

    WHERE VERSION = 604)

高效

    SELECT TAB_NAME  

    FROM TABLES  

    WHERE (TAB_NAME,DB_VER)  

    = ( SELECT TAB_NAME,DB_VER)  

    FROM TAB_COLUMNS  

    WHERE VERSION = 604)

9.        使用表的別名ALIAS

當在 SQL語句中連接多個表時, 請使用表的別名並把別名前綴於每個 Column上。這

樣一來,就可以減少解析的時間並減少那些由 Column歧義引起的語法錯誤。

(Column歧義指的是由於 SQL中不同的表具有相同的 Column名,當 SQL語句中出現這個 Column時,SQL解析器無法判斷這個 Column的歸屬)

10.    EXISTS替代IN

在許多基於基礎表的查詢中,爲了滿足一個條件,往往需要對另一個表進行聯接。在這

種情況下, 使用 EXISTS( NOT EXISTS)通常將提高查詢的效率。

低效:

    SELECT *  

FROM EMP (基礎表)  

WHERE EMPNO > 0  

AND DEPTNO IN (SELECT DEPTNO

FROM DEPT  

WHERE LOC = ‘MELB’)

    高效:

           SELECT *  

           FROM EMP (基礎表)  

           WHERE EMPNO > 0  

           AND EXISTS (SELECT ‘X’  

           FROM DEPT  

           WHERE DEPT.DEPTNO = EMP.DEPTNO  

           AND LOC = ‘MELB’)

11.    NOT EXISTS替代 NOT IN

在子查詢中, NOT IN子句將執行一個內部的排序和合並。  無論在哪種情況下, NOT IN都是最低效的  (因爲它對子查詢中的表執行了一個全表遍歷) 爲了避免使用 NOT IN ,我們可以把它改寫成外連接(Outer Joins) NOT EXISTS.

例如:

    SELECT …  

    FROM EMP  

    WHERE DEPT_NO NOT IN (SELECT DEPT_NO  

    FROM DEPT  

    WHERE DEPT_CAT=’A’);

    爲了提高效率。改寫爲:

(方法一: 高效)

    SELECT ….  

    FROM EMP A,DEPT B  

    WHERE A.DEPT_NO = B.DEPT(+)  

    AND B.DEPT_NO IS NULL  

    AND B.DEPT_CAT(+) = ‘A’

(方法二: 最高效)

       SELECT ….  

    FROM EMP E

       WHERE NOT EXISTS (SELECT ‘X’  

FROM DEPT D  

WHERE D.DEPT_NO = E.DEPT_NO  

AND DEPT_CAT = ‘A’);  

12.    用表連接替換EXISTS

通常來說 ,採用表連接的方式比 EXISTS 更有效率

   SELECT ENAME  

    FROM EMP E  

    WHERE EXISTS (SELECT ‘X’  

    FROM DEPT  

    WHERE DEPT_NO = E.DEPT_NO  

    AND DEPT_CAT = ‘A’);

 (更高效)

    SELECT ENAME  

    FROM DEPT D,EMP E  

    WHERE E.DEPT_NO = D.DEPT_NO  

    AND DEPT_CAT = ‘A’ ;

13.    EXISTS替換DISTINCT

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

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

例如:

低效:

    SELECT DISTINCT DEPT_NO,DEPT_N

    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);

14.    避免在索引列上使用計算

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

舉例:

低效:

   SELECT …  

   FROM DEPT  

   WHERE SAL * 12 > 25000;

高效:

   SELECT …  

   FROM DEPT  

   WHERE SAL > 25000/12;

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

15.    避免在索引列上使用NOT

通常,我們要避免在索引列上使用 NOTNOT 會產生在和在索引列上使用函數相同

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

舉例:

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

    SELECT …  

    FROM DEPT  

    WHERE DEPT_CODE NOT = 0;

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

    SELECT …  

    FROM DEPT  

    WHERE DEPT_CODE > 0;

16.    >=替代>

如果 DEPTNO 上有一個索引,

高效:

    SELECT *  

    FROM EMP  

    WHERE DEPTNO >=4

低效:

    SELECT *  

    FROM EMP  

    WHERE DEPTNO >3

兩者的區別在於, 前者 DBMS將直接跳到第一個 DEPT 等於 4的記錄而後者將首先

定位到 DEPTNO=3的記錄並且向前掃描到第一個 DEPT 大於 3的記錄。

17.    UNION替換OR (適用於索引列)

通常情況下, UNION替換 WHERE 子句中的 OR將會起到較好的效果。 對索引列使用 OR將造成全表掃描。注意, 以上規則只針對多個索引列有效。 如果有 column沒有被索引, 查詢效率可能會因爲你沒有選擇 OR而降低。 在下面的例子中,  LOC_ID REGION上都建有索引。

高效:

    SELECT LOC_ID LOC_DESC REGION  

    FROM LOCATION  

    WHERE LOC_ID = 10  

    UNION  

    SELECT LOC_ID LOC_DESC REGION  

    FROM LOCATION  

    WHERE REGION = “MELBOURNE”

低效:

    SELECT LOC_ID LOC_DESC REGION       FROM LOCATION  

    WHERE LOC_ID = 10 OR REGION = “MELBOURNE”

如果你堅持要用 OR 那就需要返回記錄最少的索引列寫在最前面。

18.    IN來替換OR

下面的查詢可以被更有效率的語句替換:

低效:

    SELECT…  

    FROM LOCATION  

    WHERE LOC_ID = 10  

    OR LOC_ID = 20  

    OR LOC_ID = 30

高效:

    SELECT…  

    FROM LOCATION  

    WHERE LOC_IN IN (102030);

:這是一條簡單易記的規則,但是實際的執行效果還須檢驗,在 ORACLE8i 下,兩者

的執行路徑似乎是相同的。

19.    避免在索引列上使用IS NULLIS NOT NULL

避免在索引中使用任何可以爲空的列,ORACLE 將無法使用該索引。對於單列索引,

如果列包含空值,索引中將不存在此記錄。 對於複合索引,如果每個列都爲空,索引中同樣不存在此記錄。  如果至少有一個列不爲空,則記錄存在於索引中。

舉例:

如果唯一性索引建立在表的 A列和 B 列上,  並且表中存在一條記錄的 A B 值爲(123null)   ORACLE 將不接受下一條具有相同 AB (123null)的記錄(插入) 然而如果所有的索引列都爲空, ORACLE 將認爲整個鍵值爲空而空不等於空。  因此你可以插入 1000條具有相同鍵值的記錄,當然它們都是空! 因爲空值不存在於索引列中,所以 WHERE 子句中對索引列進行空值比較將使ORACLE 停用該索引。

舉例:

低效:(索引失效)

    SELECT …  

    FROM DEPARTMENT       WHERE DEPT_CODE IS NOT NULL;

高效:(索引有效)

    SELECT …  

    FROM DEPARTMENT  

    WHERE DEPT_CODE >=0;

20.    UNION ALL替換 UNION( 如果有可能的話)

SQL語句需要 UNION兩個查詢結果集合時,這兩個結果集合會以 UNION-ALL的方式被合併, 然後在輸出最終結果前進行排序。 如果用 UNION ALL替代 UNION 這樣排序就不是必要了。 效率就會因此得到提高。

舉例:

低效:

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’  

    UNION  

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’

高效:

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’   

    UNION ALL  

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’

:需要注意的是,UNION ALL 將重複輸出兩個結果集合中相同記錄。 因此各位還是

要從業務需求分析使用 UNION ALL 的可行性。UNION 將對結果集合排序,這個操作會使用到 SORT_AREA_SIZE這塊內存。 對於這塊內存的優化也是相當重要的。 下面的 SQL可以用來查詢排序的消耗量

    Select substrname125  "Sort Area Name"

      substrvalue115    "Value"  

      from v$sysstat  

      where name like 'sort%'

21.    優化GROUP BY

提高 GROUP BY 語句的效率,  可以通過將不需要的記錄在 GROUP BY 之前過濾掉。下面兩個查詢返回相同結果但第二個明顯就快了許多。

低效:

    SELECT JOB AVG(SAL)  

    FROM EMP  

    GROUP by JOB  

    HAVING JOB = ‘PRESIDENT’  

    OR JOB = ‘MANAGER’

高效:

    SELECT JOB AVG(SAL)  

    FROM EMP  

    WHERE JOB = ‘PRESIDENT’      

OR JOB = ‘MANAGER’GROUP by JOB

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章