ORACLE SQL性能優化系列 (十三) (十四) 完結篇

43.       WHERE替代ORDER BY

ORDER BY 子句只在兩種嚴格的條件下使用索引.

 

ORDER BY中所有的列必須包含在相同的索引中並保持在索引中的排列順序.

ORDER BY中所有的列必須定義爲非空.

 

WHERE子句使用的索引和ORDER BY子句中所使用的索引不能並列.

 

例如:

      DEPT包含以下列:

 

       

DEPT_CODE    PK
    NOT NULL

        DEPT_DESC           NOT NULL

        DEPT_TYPE           NULL

   

       非唯一性的索引(DEPT_TYPE)

 

     低效: (索引不被使用)

            SELECT DEPT_CODE

            FROM DEPT

            ORDER BY DEPT_TYPE

 

       EXPLAIN PLAN:

            SORT ORDER BY

                  TABLE ACCESS FULL

 

     高效: (使用索引)

 

           SELECT DEPT_CODE

           FROM DEPT

        WHERE DEPT_TYPE > 0

  

 EXPLAIN PLAN:

      TABLE ACCESS BY ROWID ON EMP

             INDEX RANGE SCAN ON DEPT_IDX

譯者按:

      ORDER BY 也能使用索引! 這的確是個容易被忽視的知識點. 我們來驗證一下:

SQL>  select * from emp order by empno;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP'

   2    1     INDEX (FULL SCAN) OF 'EMPNO' (UNIQUE)

 

44.       避免改變索引列的類型.

當比較不同數據類型的數據時, ORACLE自動對列進行簡單的類型轉換.

 

假設 EMPNO是一個數值類型的索引列.

 

SELECT …

FROM EMP

WHERE EMPNO = ‘123’

 

實際上,經過ORACLE類型轉換, 語句轉化爲:

SELECT …

FROM EMP

WHERE EMPNO = TO_NUMBER(‘123’)

 

幸運的是,類型轉換沒有發生在索引列上,索引的用途沒有被改變.

 

現在,假設EMP_TYPE是一個字符類型的索引列.

SELECT …

FROM EMP

WHERE EMP_TYPE = 123

 

這個語句被ORACLE轉換爲:

SELECT …

FROM EMP

WHERE TO_NUMBER(EMP_TYPE)=123

 

因爲內部發生的類型轉換, 這個索引將不會被用到!

譯者按:

爲了避免ORACLE對你的SQL進行隱式的類型轉換, 最好把類型轉換用顯式表現出來. 注意當字符和數值比較時, ORACLE會優先轉換數值類型到字符類型.

 

45.       需要當心的WHERE子句

某些SELECT 語句中的WHERE子句不使用索引. 這裏有一些例子.

在下面的例子裏, ‘!=’ 將不使用索引. 記住, 索引只能告訴你什麼存在於表中, 而不能告訴你什麼不存在於表中.

不使用索引:

SELECT ACCOUNT_NAME

FROM TRANSACTION

WHERE AMOUNT !=0;

使用索引:

SELECT ACCOUNT_NAME

FROM TRANSACTION

WHERE AMOUNT >0;

 

下面的例子中, ‘||’是字符連接函數. 就象其他函數那樣, 停用了索引.

不使用索引:

SELECT ACCOUNT_NAME,AMOUNT

FROM TRANSACTION

WHERE ACCOUNT_NAME||ACCOUNT_TYPE=’AMEXA’;

使用索引:

SELECT ACCOUNT_NAME,AMOUNT

FROM TRANSACTION

WHERE ACCOUNT_NAME = ‘AMEX’

AND  ACCOUNT_TYPE=’ A’;

 

下面的例子中, ‘+’是數學函數. 就象其他數學函數那樣, 停用了索引.

不使用索引:

SELECT ACCOUNT_NAME, AMOUNT

FROM TRANSACTION

WHERE AMOUNT + 3000 >5000;

使用索引:

SELECT ACCOUNT_NAME, AMOUNT

FROM TRANSACTION

WHERE AMOUNT > 2000 ;

下面的例子中,相同的索引列不能互相比較,這將會啓用全表掃描.

不使用索引:

SELECT ACCOUNT_NAME, AMOUNT

FROM TRANSACTION

WHERE ACCOUNT_NAME = NVL(:ACC_NAME,ACCOUNT_NAME);

使用索引:

SELECT ACCOUNT_NAME, AMOUNT

FROM TRANSACTION

WHERE ACCOUNT_NAME LIKE NVL(:ACC_NAME,’%’);

 

譯者按:

如果一定要對使用函數的列啓用索引, ORACLE新的功能: 基於函數的索引(Function-Based Index) 也許是一個較好的方案.

 CREATE INDEX EMP_I ON EMP (UPPER(ename)); /*建立基於函數的索引*/

 SELECT * FROM emp WHERE UPPER(ename) = ‘BLACKSNAIL’; /*將使用索引*/


46.       連接多個掃描

如果你對一個列和一組有限的值進行比較, 優化器可能執行多次掃描並對結果進行合併連接.

舉例:

    SELECT *

    FROM LODGING

    WHERE MANAGER IN (‘BILL GATES’,’KEN MULLER’);

 

    優化器可能將它轉換成以下形式

    SELECT *

    FROM LODGING

    WHERE MANAGER = ‘BILL GATES’

    OR MANAGER = ’KEN MULLER’;

   

    當選擇執行路徑時, 優化器可能對每個條件採用LODGING$MANAGER上的索引範圍掃描. 返回的ROWID用來訪問LODGING表的記錄 (通過TABLE ACCESS BY ROWID 的方式). 最後兩組記錄以連接(CONCATENATION)的形式被組合成一個單一的集合.

 

Explain Plan :

 

SELECT STATEMENT Optimizer=CHOOSE

   CONCATENATION

      TABLE ACCESS (BY INDEX ROWID) OF LODGING

         INDEX (RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE)

     TABLE ACCESS (BY INDEX ROWID) OF LODGING

         INDEX (RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE)

譯者按:

本節和第37節似乎有矛盾之處.

 

 

47.       CBO下使用更具選擇性的索引

基於成本的優化器(CBO, Cost-Based Optimizer)對索引的選擇性進行判斷來決定索引的使用是否能提高效率.

如果索引有很高的選擇性, 那就是說對於每個不重複的索引鍵值,只對應數量很少的記錄.

比如, 表中共有100條記錄而其中有80個不重複的索引鍵值. 這個索引的選擇性就是80/100 = 0.8 . 選擇性越高, 通過索引鍵值檢索出的記錄就越少.

如果索引的選擇性很低, 檢索數據就需要大量的索引範圍查詢操作和ROWID 訪問表的

操作. 也許會比全表掃描的效率更低.

  

譯者按:

下列經驗請參閱:

a.       如果檢索數據量超過30%的表中記錄數.使用索引將沒有顯著的效率提高.

b.       在特定情況下, 使用索引也許會比全表掃描慢, 但這是同一個數量級上的

區別. 而通常情況下,使用索引比全表掃描要塊幾倍乃至幾千倍!

 

 

48.       避免使用耗費資源的操作

帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BYSQL語句會啓動SQL引擎

執行耗費資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執行兩次排序.

例如,一個UNION查詢,其中每個查詢都帶有GROUP BY子句, GROUP BY會觸發嵌入排序(NESTED SORT) ; 這樣, 每個查詢需要執行一次排序, 然後在執行UNION, 又一個唯一排序(SORT UNIQUE)操作被執行而且它只能在前面的嵌入排序結束後才能開始執行. 嵌入的排序的深度會大大影響查詢的效率.

通常, 帶有UNION, MINUS , INTERSECTSQL語句都可以用其他方式重寫.

譯者按:

     如果你的數據庫的SORT_AREA_SIZE調配得好, 使用UNION , MINUS, INTERSECT也是可以考慮的, 畢竟它們的可讀性很強

 

 

49.       優化GROUP BY

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

 

低效:

   SELECT JOB , AVG(SAL)

   FROM EMP

   GROUP JOB

   HAVING JOB = ‘PRESIDENT’

   OR JOB = ‘MANAGER’

 高效:

   SELECT JOB , AVG(SAL)

   FROM EMP

   WHERE JOB = ‘PRESIDENT’

   OR JOB = ‘MANAGER’

   GROUP JOB

     

譯者按:

     本節和14節相同. 可略過.

 

50.       使用日期

當使用日期是,需要注意如果有超過5位小數加到日期上, 這個日期會進到下一天!

 

例如:

1.

SELECT TO_DATE(‘01-JAN-93’+.99999)

FROM DUAL;

 

Returns:

   01-JAN-93 23:59:59’

2.

SELECT TO_DATE(‘01-JAN-93’+.999999)

FROM DUAL;

 

Returns:

   02-JAN-93 00:00:00

 

譯者按:

    雖然本節和SQL性能優化沒有關係, 但是作者的功力可見一斑

   

51.   使用顯式的遊標(CURSORs)

使用隱式的遊標,將會執行兩次操作. 第一次檢索記錄, 第二次檢查TOO MANY ROWS 這個exception . 而顯式遊標不執行第二次操作.

 

52.   優化EXPORTIMPORT

使用較大的BUFFER(比如10MB , 10,240,000)可以提高EXPORTIMPORT的速度.

ORACLE將盡可能地獲取你所指定的內存大小,即使在內存不滿足,也不會報錯.這個值至少要和表中最大的列相當,否則列值會被截斷.

譯者按:

可以肯定的是, 增加BUFFER會大大提高EXPORT , IMPORT的效率. (曾經碰到過一個CASE, 增加BUFFER,IMPORT/EXPORT快了10!)

作者可能犯了一個錯誤: “這個值至少要和表中最大的列相當,否則列值會被截斷. “

其中最大的列也許是指最大的記錄大小.

關於EXPORT/IMPORT的優化,CSDN論壇中有一些總結性的貼子,比如關於BUFFER參數, COMMIT參數等等, 詳情請查.

 

53.   分離表和索引

總是將你的表和索引建立在不同的表空間內(TABLESPACES). 決不要將不屬於ORACLE內部系統的對象存放到SYSTEM表空間裏. 同時,確保數據表空間和索引表空間置於不同的硬盤上.

 

譯者按:

同時,確保數據表空間和索引表空間置與不同的硬盤上.”可能改爲如下更爲準確同時,確保數據表空間和索引表空間置與不同的硬盤控制卡控制的硬盤上.”

 

 

(全文完)

                                Black_Snail

                                [email protected]

                                4/Sep/2003 

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