最近系統的研究了一下ORACLE SQL語句性能調優,在此大言不慚的得出一個觀點——網上不少性能調優的結論都是錯誤的或者不周全的。
如今的DBA大牛些都過低調了,不出來斧正一下,小弟來借這個機會吐槽一下,說的不對,歡迎拍磚,特別是版本問題:網絡
轉入正題:
網絡上大部分結論「可能」適用於ORACLE8或者之前版本(小弟出道晚沒見過ORACLE 8),可是針對9i及之後的版本,不少結論都是欠周全的。oracle
下面舉幾個最多見的問題:ide
錯誤觀點一、何時用IN 何時用EXISTS? 子查詢數據量少用IN 量多用EXISTS
若是你說 半鏈接子查詢返回數據少的時候用IN,返回數據多的時候用EXISTS,那麼恭喜你,你錯了 。函數
糾正:不要輕信網上是如何說的,學了ORACLE最大的感觸就是不要記結論,本身實踐了才知道,建立兩張表練習一下,因爲篇幅緣由,我這裏仍是給一個結論——經過執行計劃看到,大多數狀況下IN 和EXISTS的效率是如出一轍的,只是有時候EXISTS不能 SUBSTRING UNNESTING,致使執行計劃走FILTER ,執行計劃一旦走了FILTER,驅動表是改變不了的(12C 能不能我不清楚),想象一下,主表是1000W,子表返回20條,因爲驅動表改變不了,頗有多是大表驅動小表了。
錯誤觀點二、NOT IN 與 NOT EXISTS 子查詢量少用NOT IN 不然用NOT EXISTS
NOT IN 與 NOT EXISTS也是一個道理,可是要注意NULL的狀況,NULL容易致使沒法使用索引,能夠建立函數索引或與常量一塊兒作一個組合索引。性能
錯誤觀點三、WHERE條件有前後順序,後面的先執行或前面的先執行
若是過濾數據量基本持平的話,兩個不一樣的謂詞過濾條件可能會由於腳本的編寫而有前後順序,可是不要所以推斷出「WHERE條件有前後順序,後面的先執行或前面的先執行」,哪一個先執行時CBO根據統計信息分析以後說了算,下面兩條語句的執行計劃的邏輯讀都是同樣的
能夠經過下面的列子來測試一下執行效果測試
- CREATE TABLE TEST02 AS SELECT * FROM DBA_OBJECTS;
- SELECT COUNT(*) FROM TEST02 A WHERE A.OWNER='SYS' AND A.OBJECT_ID=29 ;
- SELECT COUNT(*) FROM TEST02 A WHERE A.OBJECT_ID=29 AND A.OWNER='SYS' ;
錯誤觀點四、FROM 語句有左右順序 因此要注意書寫順序
同第3點同樣,給個例子——下面兩個語句的執行計劃也是同樣的,ORACLE知道哪一個該作驅動表 ,所以沒有區別優化
- CREATE TABLE T1 AS SELECT LEVEL AS ID FROM DUAL CONNECT BY LEVEL<=10000;--大表
- CREATE TABLE T2 AS SELECT LEVEL AS ID FROM DUAL CONNECT BY LEVEL<=10;--小表
- SELECT COUNT(1) FROM T1,T2;
- SELECT COUNT(1) FROM T2,T1;
-
- 執行計劃
- ----------------------------------------------------------
- Plan hash value: 4259280259
-
- ----------------------------------------------------------------------
- | Id | Operation | Name | Rows | Cost (%CPU)| Time |
- ----------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 60 (2)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | | |
- | 2 | MERGE JOIN CARTESIAN| | 100K| 60 (2)| 00:00:01 |
- | 3 | TABLE ACCESS FULL | T2 | 10 | 3 (0)| 00:00:01 |
- | 4 | BUFFER SORT | | 10000 | 57 (2)| 00:00:01 |
- | 5 | TABLE ACCESS FULL | T1 | 10000 | 6 (0)| 00:00:01 |
- ----------------------------------------------------------------------
- 統計信息
- ----------------------------------------------------------
- 0 recursive calls
- 0 db block gets
- 22 consistent gets
- 0 physical reads
- 0 redo size
- 527 bytes sent via SQL*Net to client
- 519 bytes received via SQL*Net from client
- 2 SQL*Net roundtrips to/from client
- 1 sorts (memory)
- 0 sorts (disk)
- 1 rows processed
錯誤觀點五、避免使用OR來鏈接條件,不然致使引擎放棄使用索引而進行全表掃描
如:SELECT ID FROM T WHERE NUM=10 OR NUM=11 會全表掃描spa
下面來個例子,orm
- CREATE TABLE TEST02 AS SELECT * FROM DBA_OBJECTS;
- CREATE INDEX TEST_02_IDX_01 ON TEST02 (OBJECT_ID);
- ALTER SESSION SET OPTIMIZER_FEATURES_ENABLE='9.2.0';
- SELECT COUNT(*) FROM TEST02 A WHERE A.OBJECT_ID=28 OR A.OBJECT_ID=29 ;
查看執行計劃索引
- 執行計劃
- ----------------------------------------------------------
- Plan hash value: 3430686514
- ---------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost |
- ---------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 5 | 2 |
- | 1 | SORT AGGREGATE | | 1 | 5 | |
- | 2 | INLIST ITERATOR | | | | |
- |* 3 | INDEX RANGE SCAN| TEST_02_IDX_01 | 2 | 10 | 2 |
- ---------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 3 - access("A"."OBJECT_ID"=28 OR "A"."OBJECT_ID"=29)
實際上使用了索引
總結:網上的經驗多是某些人看了某些文章,而後在特定的場景下測試了效果同樣,甚至沒有測試就以訛傳訛,容易誤導人,
Oracle腳本中有多優化的觀點不一樣的版本等場景下由於執行計劃的不一樣 而致使結論不同,所以要根據實際出發,最好再親自測一下,DBA不會告訴你這麼的,由於他們本身還要混飯吃的。
不過說不定我這盤文章中有些結論也是錯的,本身測一遍才知道
其實ORACLE優化仍是有不少小細節須要注意的,也有不少的方法,有機會的話再與你們分享