數據庫性能優化之SQL語句優化(下)

數據庫性能優化之SQL語句優化(下)

(1) 選擇最有效率的表名順序(只在基於規則的優化器中有效):java

ORACLE 的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)將被最早處理,在FROM子句中包含多個表的狀況下,你必須選擇記錄條數最少的表做爲基礎表。若是有3個以上的錶鏈接查詢, 那就須要選擇交叉表(intersection table)做爲基礎表, 交叉表是指那個被其餘表所引用的表.sql

(2) WHERE子句中的鏈接順序:數據庫

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

(3) SELECT子句中避免使用 ‘ * ‘:性能優化

ORACLE在解析的過程當中, 會將’*’ 依次轉換成全部的列名, 這個工做是經過查詢數據字典完成的, 這意味着將耗費更多的時間。oracle

(4) 減小訪問數據庫的次數:函數

ORACLE在內部執行了許多工做: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數據塊等。工具

(5) 在SQL*Plus , SQL*Forms和Pro*C中從新設置ARRAYSIZE參數, 能夠增長每次數據庫訪問的檢索數據量 ,建議值爲200。性能

(6) 使用DECODE函數來減小處理時間:優化

使用DECODE函數能夠避免重複掃描相同記錄或重複鏈接相同的表.

(7) 整合簡單,無關聯的數據庫訪問:

若是你有幾個簡單的數據庫查詢語句,你能夠把它們整合到一個查詢中(即便它們之間沒有關係) 。

(8) 刪除重複記錄:

最高效的刪除重複記錄方法 ( 由於使用了ROWID)例子:

  1. DELETE  FROM  EMP E  WHERE  E.ROWID >   
  2. (SELECT MIN(X.ROWID) FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO) 

(9) 用TRUNCATE替代DELETE:

當刪除表中的記錄時,在一般狀況下, 回滾段(rollback segments ) 用來存放能夠被恢復的信息. 若是你沒有COMMIT事務,ORACLE會將數據恢復到刪除以前的狀態(準確地說是恢復到執行刪除命令以前的情況) 而當運用TRUNCATE時, 回滾段再也不存聽任何可被恢復的信息.當命令運行後,數據不能被恢復.所以不多的資源被調用,執行時間也會很短. (譯者按: TRUNCATE只在刪除全表適用,TRUNCATE是DDL不是DML) 。

(10) 儘可能多使用COMMIT:

只要有可能,在程序中儘可能多使用COMMIT, 這樣程序的性能獲得提升,需求也會由於COMMIT所釋放的資源而減小,COMMIT所釋放的資源:

a. 回滾段上用於恢復數據的信息.

b. 被程序語句得到的鎖

c. redo log buffer 中的空間

d. ORACLE爲管理上述3種資源中的內部花費

(11) 用Where子句替換HAVING子句:

避免使用HAVING子句, HAVING 只會在檢索出全部記錄以後纔對結果集進行過濾. 這個處理須要排序,總計等操做. 若是能經過WHERE子句限制記錄的數目,那就能減小這方面的開銷.

(非oracle中)on、where、having這三個均可以加條件的子句中,on是最早執行,where次之,having最後,由於on是先把不符合條件的記錄過濾後才進行統計,它就能夠減小中間運算要處理的數據,按理說應該速度是最快的,where也應該比having快點的,由於它過濾數據後才進行sum,在兩個表聯接時才用on的,因此在一個表的時候,就剩下where跟having比較了。

在這單表查詢統計的狀況下,若是要過濾的條件沒有涉及到要計算字段,那它們的結果是同樣的,只是where可使用rushmore技術,而having就不能,在速度上後者要慢若是要涉及到計算的字 段,就表示在沒計算以前,這個字段的值是不肯定的,根據上篇寫的工做流程,where的做用時間是在計算以前就完成的,而having就是在計算後才起做 用的,因此在這種狀況下,二者的結果會不一樣。

在多表聯接查詢時,on比where更早起做用。系統首先根據各個表之間的聯接條件,把多個表合成一個臨時表 後,再由where進行過濾,而後再計算,計算完後再由having進行過濾。因而可知,要想過濾條件起到正確的做用,首先要明白這個條件應該在何時起做用,而後再決定放在那裏。

(12) 減小對錶的查詢:

在含有子查詢的SQL語句中,要特別注意減小對錶的查詢.例子:

  1. SELECT  TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) =   
  2. SELECT TAB_NAME,DB_VER FROM  TAB_COLUMNS  WHERE  VERSION = 604) 

(13) 經過內部函數提升SQL效率:

複雜的SQL每每犧牲了執行效率. 可以掌握上面的運用函數解決問題的方法在實際工做中是很是有意義的。

(14) 使用表的別名(Alias):

當在SQL語句中鏈接多個表時, 請使用表的別名並把別名前綴於每一個Column上.這樣一來,就能夠減小解析的時間並減小那些由Column歧義引發的語法錯誤。

(15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN:

在許多基於基礎表的查詢中,爲了知足一個條件,每每須要對另外一個表進行聯接.在這種狀況下, 使用EXISTS(或NOT EXISTS)一般將提升查詢的效率. 在子查詢中,NOT IN子句將執行一個內部的排序和合並. 不管在哪一種狀況下,NOT IN都是最低效的 (由於它對子查詢中的表執行了一個全表遍歷). 爲了不使用NOT IN ,咱們能夠把它改寫成外鏈接(Outer Joins)或NOT EXISTS。

例子:

(高效)

  1. SELECT * FROM  EMP  

(基礎表)

  1. WHERE  EMPNO > 0  AND  EXISTS   
  2. (SELECT ‘X'  FROM DEPT  WHERE  DEPT.DEPTNO = EMP.DEPTNO    
  3. AND  LOC = ‘MELB')  

(低效)

  1. SELECT  * FROM  EMP  

(基礎表)

  1. WHERE  EMPNO > 0  AND  DEPTNO IN(SELECT DEPTNO  FROM    
  2. DEPT  WHERE  LOC = ‘MELB') 

(16) 識別’低效執行’的SQL語句:

雖然目前各類關於SQL優化的圖形化工具層出不窮,可是寫出本身的SQL工具來解決問題始終是一個最好的方法:

  1. SELECT  EXECUTIONS , DISK_READS, BUFFER_GETS,   
  2. ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,   
  3. ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,   
  4. SQL_TEXT   
  5. FROM  V$SQLAREA   
  6. WHERE  EXECUTIONS>0   
  7. AND  BUFFER_GETS > 0   
  8. AND  (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8   
  9. ORDER BY  4 DESC; 

(17) 用索引提升效率:

索引是表的一個概念部分,用來提升檢索數據的效率,ORACLE使用了一個複雜的自平衡B-tree結構. 一般,經過索引查詢數據比全表掃描要快. 當ORACLE找出執行查詢和Update語句的最佳路徑時, ORACLE優化器將使用索引.

一樣在聯結多個表時使用索引也能夠提升效率. 另外一個使用索引的好處是,它提供了主鍵(primary key)的惟一性驗證.。那些LONG或LONG RAW數據類型, 你能夠索引幾乎全部的列. 一般, 在大型表中使用索引特別有效. 固然,你也會發現, 在掃描小表時,使用索引一樣能提升效率.

雖然使用索引能獲得查詢效率的提升,可是咱們也必須注意到它的代價. 索引須要空間來存儲,也須要按期維護, 每當有記錄在表中增減或索引列被修改時, 索引自己也會被修改. 這意味着每條記錄的INSERT , DELETE , UPDATE將爲此多付出4 , 5 次的磁盤I/O . 由於索引須要額外的存儲空間和處理,那些沒必要要的索引反而會使查詢反應時間變慢.。按期的重構索引是有必要的:

  1. ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME> 

(18) 用EXISTS替換DISTINCT:

當提交一個包含一對多表信息(好比部門表和僱員表)的查詢時,避免在SELECT子句中使用DISTINCT. 通常能夠考慮用EXIST替換, EXISTS 使查詢更爲迅速,由於RDBMS核心模塊將在子查詢的條件一旦知足後,馬上返回結果. 例子:

(低效):

  1. SELECT  DISTINCT  DEPT_NO,DEPT_NAME  FROM    
  2. DEPT D , EMP E WHERE  D.DEPT_NO = E.DEPT_NO  

(高效):

  1. SELECT  DEPT_NO,DEPT_NAME  FROM  DEPT D  WHERE  EXISTS   
  2. SELECT ‘X'  FROM  EMP E  WHERE E.DEPT_NO = D.DEPT_NO); 

(19) sql語句用大寫的;由於oracle老是先解析sql語句,把小寫的字母轉換成大寫的再執行。

(20) 在java代碼中儘可能少用鏈接符「+」鏈接字符串!

(21) 避免在索引列上使用NOT,一般咱們要避免在索引列上使用NOT, NOT會產生在和在索引列上使用函數相同的影響. 當ORACLE」遇到」NOT,他就會中止使用索引轉而執行全表掃描。

(22) 避免在索引列上使用計算

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

低效:

  1. SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;  

高效:

  1. SELECT … FROM DEPT WHERE SAL > 25000/12; 

(23) 用>=替代>

高效:

  1. SELECT * FROM  EMP  WHERE  DEPTNO >=4  

低效:

  1. SELECT * FROM EMP WHERE DEPTNO >3 

二者的區別在於, 前者DBMS將直接跳到第一個DEPT等於4的記錄然後者將首先定位到DEPTNO=3的記錄而且向前掃描到第一個DEPT大於3的記錄。

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

一般狀況下, 用UNION替換WHERE子句中的OR將會起到較好的效果. 對索引列使用OR將形成全表掃描. 注意, 以上規則只針對多個索引列有效. 若是有column沒有被索引, 查詢效率可能會由於你沒有選擇OR而下降. 在下面的例子中, LOC_ID 和REGION上都建有索引.

高效:

  1. SELECT LOC_ID , LOC_DESC , REGION   
  2. FROM LOCATION   
  3. WHERE LOC_ID = 10   
  4. UNION   
  5. SELECT LOC_ID , LOC_DESC , REGION   
  6. FROM LOCATION   
  7. WHERE REGION = 「MELBOURNE」  

低效:

  1. SELECT LOC_ID , LOC_DESC , REGION   
  2. FROM LOCATION   
  3. WHERE LOC_ID = 10 OR REGION = 「MELBOURNE」 

若是你堅持要用OR, 那就須要返回記錄最少的索引列寫在最前面.

(25) 用IN來替換OR

這是一條簡單易記的規則,可是實際的執行效果還須檢驗,在ORACLE8i下,二者的執行路徑彷佛是相同的.

低效:

  1. SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30  

高效

  1. SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30); 

(26) 避免在索引列上使用IS NULL和IS NOT NULL

避免在索引中使用任何能夠爲空的列,ORACLE將沒法使用該索引.對於單列索引,若是列包含空值,索引中將不存在此記錄. 對於複合索引,若是每一個列都爲空,索引中一樣不存在此記錄. 若是至少有一個列不爲空,則記錄存在於索引中.

舉例: 若是惟一性索引創建在表的A列和B列上, 而且表中存在一條記錄的A,B值爲(123,null) , ORACLE將不接受下一條具備相同A,B值(123,null)的記錄(插入). 然而若是全部的索引列都爲空,ORACLE將認爲整個鍵值爲空而空不等於空. 所以你能夠插入1000 條具備相同鍵值的記錄,固然它們都是空! 由於空值不存在於索引列中,因此WHERE子句中對索引列進行空值比較將使ORACLE停用該索引。

低效: (索引失效)

  1. SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;  

高效: (索引有效)

  1. SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0; 

(27) 老是使用索引的第一個列:

若是索引是創建在多個列上, 只有在它的第一個列(leading column)被where子句引用時,優化器纔會選擇使用該索引. 這也是一條簡單而重要的規則,當僅引用索引的第二個列時,優化器使用了全表掃描而忽略了索引。

(28) 用UNION-ALL 替換UNION ( 若是有可能的話):

當SQL 語句須要UNION兩個查詢結果集合時,這兩個結果集合會以UNION-ALL的方式被合併, 而後在輸出最終結果前進行排序. 若是用UNION ALL替代UNION, 這樣排序就不是必要了. 效率就會所以獲得提升.

須要注意的是,UNION ALL 將重複輸出兩個結果集合中相同記錄. 所以各位仍是要從業務需求分析使用UNION ALL的可行性. UNION 將對結果集合排序,這個操做會使用到SORT_AREA_SIZE這塊內存. 對於這塊內存的優化也是至關重要的. 下面的SQL能夠用來查詢排序的消耗量。

低效:

  1. SELECT  ACCT_NUM, BALANCE_AMT   
  2. FROM  DEBIT_TRANSACTIONS   
  3. WHERE TRAN_DATE = '31-DEC-95'  
  4. UNION   
  5. SELECT ACCT_NUM, BALANCE_AMT   
  6. FROM DEBIT_TRANSACTIONS   
  7. WHERE TRAN_DATE = '31-DEC-95'  

高效:

  1. SELECT ACCT_NUM, BALANCE_AMT   
  2. FROM DEBIT_TRANSACTIONS   
  3. WHERE TRAN_DATE = '31-DEC-95'   
  4. UNION ALL   
  5. SELECT ACCT_NUM, BALANCE_AMT   
  6. FROM DEBIT_TRANSACTIONS   
  7. WHERE TRAN_DATE = '31-DEC-95' 

(29) 用WHERE替代ORDER BY:

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

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

ORDER BY中全部的列必須定義爲非空.

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

例如:

表DEPT包含如下列:

  1. DEPT_CODE PK NOT NULL   
  2. DEPT_DESC NOT NULL   
  3. DEPT_TYPE NULL 

低效: (索引不被使用)

  1. SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE  

高效: (使用索引)

  1. SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0 

(30) 避免改變索引列的類型:

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

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

  1. SELECT …  FROM EMP  WHERE  EMPNO = ‘123' 

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

  1. SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER(‘123') 

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

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

  1. SELECT …  FROM EMP  WHERE EMP_TYPE = 123 

這個語句被ORACLE轉換爲:

  1. SELECT …  FROM EMP  WHERE TO_NUMBER(EMP_TYPE)=123 

由於內部發生的類型轉換, 這個索引將不會被用到! 爲了不ORACLE對你的SQL進行隱式的類型轉換, 最好把類型轉換用顯式表現出來. 注意當字符和數值比較時, ORACLE會優先轉換數值類型到字符類型。如:

  1. select   emp_name   form   employee   where   salary   >   3000 

在此語句中若salary是Float類型的,則優化器對其進行優化爲Convert(float,3000),由於3000是個整數,咱們應在編程時使用3000.0而不要等運行時讓DBMS進行轉化。一樣字符和整型數據的轉換。

(31) 須要小心的WHERE子句:

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

在下面的例子裏:

(1)‘!=’ 將不使用索引. 記住, 索引只能告訴你什麼存在於表中, 而不能告訴你什麼不存在於表中.

(2) ‘ ¦ ¦’是字符鏈接函數. 就象其餘函數那樣, 停用了索引.

(3) ‘+’是數學函數. 就象其餘數學函數那樣, 停用了索引.

(4)相同的索引列不能互相比較,這將會啓用全表掃描.

(32)一般狀況下,使用索引比全表掃描要塊幾倍乃至幾千倍!

a. 若是檢索數據量超過30%的表中記錄數.使用索引將沒有顯著的效率提升.  b. 在特定狀況下, 使用索引也許會比全表掃描慢, 但這是同一個數量級上的區別.

(33) 避免使用耗費資源的操做:

帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啓動SQL引擎執行耗費資源的排序(SORT)功能. DISTINCT須要一次排序操做, 而其餘的至少須要執行兩次排序.

一般, 帶有UNION, MINUS , INTERSECT的SQL語句均可以用其餘方式重寫. 若是你的數據庫的SORT_AREA_SIZE調配得好, 使用UNION , MINUS, INTERSECT也是能夠考慮的, 畢竟它們的可讀性很強。

(34) 優化GROUP BY:

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

低效:

  1. SELECT JOB , AVG(SAL)   
  2. FROM EMP   
  3. GROUP by JOB   
  4. HAVING JOB = ‘PRESIDENT'   
  5. OR JOB = ‘MANAGER'  

高效:

  1. SELECT JOB , AVG(SAL)   
  2. FROM EMP   
  3. WHERE JOB = ‘PRESIDENT'   
  4. OR JOB = ‘MANAGER'   
  5. GROUP by JOB  
相關文章
相關標籤/搜索