數據查詢優化的方法

原文:http://blog.csdn.net/nileel/archive/2008/06/03/2509019.aspx

1.       用IN來替換OR
下面的查詢可以被更有效率的語句替換:
低效:
SELECT field1, field1 FROM LOCATION
WHERE LOC_ID = 10 OR     LOC_ID = 20 OR     LOC_ID = 30

高效
SELECT field1, field1 FROM LOCATION
WHERE LOC_IN IN (10,20,30)    
2.       連接多個掃描
如果你對一個列和一組有限的值進行比較, 優化器可能執行多次掃描並對結果進行合併連接.
舉例:
    SELECT * FROM LODGING
    WHERE MANAGER IN (‘BILL GATES’,’KEN MULLER’);
 
    優化器可能將它轉換成以下形式
    SELECT *  FROM LODGING
    WHERE MANAGER = ‘BILL GATES’
    OR MANAGER = ’KEN MULLER’;
3.       優化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  
4.       用>=替代>
如果DEPTNO上有一個索引, 
高效:
   SELECT *
   FROM EMP
   WHERE DEPTNO >=4
   
   低效:
   SELECT *
   FROM EMP
   WHERE DEPTNO >3

5.       用表連接替換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’ ; 
6.       用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核心模塊將在子查詢的條件一旦滿足後,立刻返回結果.
7.       使用表的別名(Alias)
當在SQL語句中連接多個表時, 請使用表的別名並把別名前綴於每個Column上.這樣一來,就可以減少解析的時間並減少那些由Column歧義引起的語法錯誤.
(譯者注: Column歧義指的是由於SQL中不同的表具有相同的Column名,當SQL語句中出現這個Column時,SQL解析器無法判斷這個Column的歸屬)

8.       用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’)
 (譯者按: 相對來說,用NOT EXISTS替換NOT IN 將更顯著地提高效率,下一節中將指出)
9.       用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’);
10.       減少對錶的查詢
在含有子查詢的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)
     Update 多個Column 例子:
     低效:
           UPDATE EMP
           SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES),
              SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES)
           WHERE EMP_DEPT = 0020;
     高效:
           UPDATE EMP
           SET (EMP_CAT, SAL_RANGE) 
= (SELECT MAX(CATEGORY) , MAX(SAL_RANGE)
 FROM EMP_CATEGORIES)
           WHERE EMP_DEPT = 0020;
11. 在Oracle快速進行數據行存在性檢查
只檢索一個啓示就可以判斷主鍵是否能與外鍵相配,這比Count(*)方法快得多,例如: 
SQL Using Count(*) 
    SELECT Count(*) INTO :ll_Count
       FROM ORDER
       WHERE PROD_ID = :ls_CheckProd
       USING SQLCA;
    
    IF ll_Count > 0 THEN // Cannot delete product 
SQL Using ROWNUM
   SELECT ORDER_ID INTO :ll_OrderID
       FROM ORDER
       WHERE PROD_ID = :ls_CheckProd
          AND ROWNUM < 2
       USING SQLCA;

    IF SQLCA.SQLNRows <> 0 THEN // cannot delete product
12 使用%TYPE、%ROWTYPE方式聲明變量
  程序設計中常常要通過變量來實現程序間的數據傳遞,即將表中數據賦值給變量,或是把變量值插入到表中。而要完成這些操作的前提就是,表中數據與變量類型要一致。然而在實際中,表中數據或類型、或寬度有時要變化,一旦變化,就必須去修改程序中的變量聲明部分,否則程序將不能正常運行。爲了減少這部分程序的修改,編程時使用%TYPE、%ROWTYPE方式聲明變量,使變量聲明的類型與表中的保持同步,隨表的變化而變化,這樣的程序在一定程度上具有更強的通用性。
13.       使用DECODE函數來減少處理時間
使用DECODE函數可以避免重複掃描相同記錄或重複連接相同的表.
例如:
   SELECT COUNT(*),SUM(SAL)
   FROM EMP
   WHERE DEPT_NO = 0020
   AND ENAME LIKE ‘SMITH%’;

   SELECT COUNT(*),SUM(SAL)
   FROM EMP
   WHERE DEPT_NO = 0030
   AND ENAME LIKE ‘SMITH%’;

你可以用DECODE函數高效地得到相同結果

SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT,
        COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT,
        SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,
        SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL
FROM EMP WHERE ENAME LIKE ‘SMITH%’;

類似的,DECODE函數也可以運用於GROUP BY 和ORDER BY子句中.
14.       儘量多使用COMMIT
只要有可能,在程序中儘量多使用COMMIT, 這樣程序的性能得到提高,需求也會因爲COMMIT所釋放的資源而減少:
 COMMIT所釋放的資源:
a.       回滾段上用於恢復數據的信息.
b.       被程序語句獲得的鎖
c.       redo log buffer 中的空間
d.       ORACLE爲管理上述3種資源中的內部花費
15.       整合簡單,無關聯的數據庫訪問
如果你有幾個簡單的數據庫查詢語句,你可以把它們整合到一個查詢中(即使它們之間沒有關係)
例如:

SELECT NAME 
FROM EMP 
WHERE EMP_NO = 1234;

SELECT NAME 
FROM DPT
WHERE DPT_NO = 10 ;

SELECT NAME 
FROM CAT
WHERE CAT_TYPE = ‘RD’;

上面的3個查詢可以被合併成一個:

SELECT E.NAME , D.NAME , C.NAME
FROM CAT C , DPT D , EMP E,DUAL X
WHERE NVL(‘X’,X.DUMMY) = NVL(‘X’,E.ROWID(+))
AND NVL(‘X’,X.DUMMY) = NVL(‘X’,D.ROWID(+))
AND NVL(‘X’,X.DUMMY) = NVL(‘X’,C.ROWID(+))
AND E.EMP_NO(+) = 1234
AND D.DEPT_NO(+) = 10
AND C.CAT_TYPE(+) = ‘RD’; 
16.       WHERE子句中的連接順序.
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’;
17.     減少訪問數據庫的次數
當執行每條SQL語句時, ORACLE在內部執行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數據塊等等. 由此可見, 減少訪問數據庫的次數 , 就能實際上減少ORACLE的工作量.

例如,
    以下有三種方法可以檢索出僱員號等於0342或0291的職員.

方法1 (最低效)
    SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 342;
     
    SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 291;

方法2 (次低效)
    
    DECLARE 
        CURSOR C1 (E_NO NUMBER) IS 
        SELECT EMP_NAME,SALARY,GRADE
        FROM EMP 
        WHERE EMP_NO = E_NO;
    BEGIN 
        OPEN C1(342);
        FETCH C1 INTO …,..,.. ;
        …..
        OPEN C1(291);
       FETCH C1 INTO …,..,.. ;
         CLOSE C1;
      END;

方法3 (高效)

    SELECT A.EMP_NAME , A.SALARY , A.GRADE,
            B.EMP_NAME , B.SALARY , B.GRADE
    FROM EMP A,EMP B
    WHERE A.EMP_NO = 342
    AND   B.EMP_NO = 291; 

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