oracle中Where子句順序是否對SQL性能有影響

常常有人問到oracle中的Where子句的條件書寫順序是否對SQL性能有影響,個人直覺是沒有影響,由於若是這個順序有影響,Oracle應該早就可以作到自動優化,但一直沒有關於這方面的確鑿證據。在網上查到的文章,通常認爲在RBO優化器模式下無影響(10G開始,缺省爲RBO優化器模式),而在CBO優化器模式下有影響,主要有兩種觀點:  

 

a.能使結果最少的條件放在最右邊,SQL執行是按從右到左進行結果集的篩選的;  b.有人試驗代表,能使結果最少的條件放在最左邊,SQL性能更高。  查過oracle8到11G的在線文檔,關於SQL優化相關章節,沒有任何文檔說過where子句中的條件對SQL性能有影響,到底哪一種觀點是對的,沒有一種確切的結論,只好本身來作實驗證實。結果代表,SQL條件的執行是從右到左的,但條件的順序對SQL性能沒有影響。    實驗一:證實了SQL的語法分析是從右到左的  下面的試驗在9i和10G均可以獲得相同的結果: 第1條語句執行不會出錯,第2條語句會提示除數不能爲零。  1.Select 'ok' From Dual Where 1 / 0 = 1 And 1 = 2;  2.Select 'ok' From Dual Where 1 = 2 And 1 / 0 = 1;  證實了SQL的語法分析是從右到左的。    實驗二:證實了SQL條件的執行是從右到左的  drop table temp;  create table temp( t1 varchar2(10),t2 varchar2(10));  insert into temp values('zm','abcde');  insert into temp values('sz','1');  insert into temp values('sz','2');  commit;  1. select * from temp where to_number(t2)>1 and t1='sz';  2. select * from temp where t1='sz' and to_number(t2)>1;  在9i上執行, 第1條語句執行不會出錯,第2條語句會提示「無效的數字」  在10G上執行,兩條語句都不會出錯。  說明:9i上,SQL條件的執行確實是從右到左的,可是10G作了什麼調整呢?  實驗三:證實了在10g上SQL條件的執行是從右到左的  Create Or Replace Function F1(v_In Varchar2) Return Varchar2 Is  Begin  Dbms_Output.Put_Line('exec F1');  Return v_In;  End F1;  /  Create Or Replace Function F2(v_In Varchar2) Return Varchar2 Is  Begin  Dbms_Output.Put_Line('exec F2');  Return v_In;  End F2;  /  SQL> set serverout on;  SQL> select 1 from dual where f1('1')='1' and f2('1')='1';  1  ----------  1  exec F2  exec F1  SQL> select 1 from dual where f2('1')='1' and f1('1')='1';  1  ----------  1  exec F1  exec F2  結果代表,SQL條件的執行順序是從右到左的。  那麼,根據這個結果來分析,把能使結果最少的條件放在最右邊,是否會減小其它條件執行時所用的記錄數量,從而提升性能呢?  例如:下面的SQL條件,是否應該調整SQL條件的順序呢?  Where A.結賬id Is Not Null  And A.記錄狀態<>0  And A.記賬費用=1  And (Nvl(A.實收金額, 0)<>Nvl(A.結賬金額, 0) Or Nvl(A.結賬金額, 0)=0)  And A.病人ID=[1] And Instr([2],','||Nvl(A.主頁ID,0)||',')>0  And A.登記時間Between [3] And [4]  And A.門診標誌<>1  實際上,從這條SQL語句的執行計劃來分析,Oracle首先會找出條件中使用索引或表間鏈接的條件,以此來過濾數據集,而後對這些結果數據塊所涉及的記錄逐一檢查是否符合全部條件,因此條件順序對性能幾乎沒有影響。  若是沒有索引和表間鏈接的狀況,條件的順序是否對性能有影響呢?再來看一個實驗。  實驗四:證實了條件的順序對性能沒有影響。  SQL> select count(*) from診療項目目錄where操做類型='1';COUNT(*)  ----------  3251  SQL> select count(*) from診療項目目錄where類別='Z';COUNT(*)  ----------  170  SQL> select count(*) from診療項目目錄where類別='Z' and操做類型='1';COUNT(*)  ----------  1  Declare  V1 Varchar2(20);  Begin  For I In 1 .. 1000 Loop  --Select名稱Into V1 From診療項目目錄Where類別= 'Z' And操做類型= '1';  select名稱Into V1 from診療項目目錄where操做類型='1' and類別='Z';  End Loop;  End;  /  上面的SQL按兩種方式分別執行了1000次查詢,結果以下:  類型= '1'在最右 | 類別='Z'在最右  0.093 | 1.014  1.06 | 0.999  0.998 | 1.014  按理說,從右到左的順序執行,「類別='Z'」在最右邊時,先過濾獲得170條記錄,再從中找符合「操做類型 = '1'」的,比較而言,「操做類型 = '1'」在最右邊時,先過濾獲得3251條記錄,再從中找符合「類別='Z'」,效率應該要低些,而實際結果倒是二者所共的時間差很少。  其實,從Oracle的數據訪問原理來分析,兩種順序的寫法,執行計劃都是同樣的,都是全表掃描,都要依次訪問該表的全部數據塊,對每個數據塊中的行,逐一檢查是否同時符合兩個條件。因此,就不存在先過濾出多少條數據的問題。    綜上所述,Where子句中條件的順序對性能沒有影響(無論是CBO仍是RBO優化器模式),注意,額外說一下,這裏只是說條件的順序,不包含表的順序。在RBO優化器模式下,表應按結果記錄數從大到小的順序從左到右來排列,由於表間鏈接時,最右邊的表會被放到嵌套循環的最外層。最外層的循環次數越少,效率越高。 
相關文章
相關標籤/搜索