in和exists以及not in 和not exists有什麼不一樣?(筆記)


exists 和 in

1.原理

經過使用EXISTS,Oracle會首先檢查主查詢,而後運行子查詢直到它找到第一個匹配項,
 這就節省了時間。Oracle在執行IN子查詢時,首先執行子查詢,並將得到的結果列表存放在一
 個加了索引的臨時表中。在執行子查詢以前,系統先將主查詢掛起,待子查詢執行完畢,存放
 在臨時表中之後再執行主查詢。這也就是使用EXISTS比使用IN一般查詢速度快的緣由。

2.分析

in 是把外表和內表做hash 鏈接,而exists是對外表做loop循環,每次loop循環再對內
 表進行查詢not exists:作NL,對子查詢先查,有個虛表,有肯定值,因此就算子查詢有NULL
 最終也有值返回not in:作hash,對子查詢表創建內存數組,用外表匹配,那子查詢要是有
 NULL那外表沒的匹配最終無值返回。一直以來認爲exists比in效率高的說法是不許確的。

可是,若是查詢的兩個表大小至關,那麼用in和exists差異不大。html

3.總結

外表大,用IN;內表大,用EXISTS。

4.效率

- select * from T1 where exists(select 1 from T2 where T1.a=T2.a) ;
    T1數據量小而T2數據量很是大時,T1<<T2 時,1) 的查詢效率高。
- select * from T1 where T1.a in (select T2.a from T2);
    T1數據量很是大而T2數據量小時,T1>>T2 時,2) 的查詢效率高。

5.舉例說明

例如:表A(小表),表B(大表) 
 1: select * from A where cc in (select cc from B) 
     效率低,用到了A表上cc列的索引; 
    select * from A where exists(select cc from B where cc=A.cc) 
    效率高,用到了B表上cc列的索引。
 
 2. select * from B where cc in (select cc from A) 
    效率高,用到了B表上cc列的索引; 
    select * from B where exists(select cc from A where cc=B.cc) 
    效率低,用到了A表上cc列的索引。

not in 和not exists

若是查詢語句使用了not in 那麼內外表都進行全表掃描,沒有用到索引;而
not extsts的子查詢依然能用到表上的索引。 
    因此不管那個表大,用not exists都比not in要快。一直聽到的都是說盡可能
用exists不要用in,由於exists只判斷存在而in須要對比值,因此exists比較快,
但其實根本不是這麼回事。

示例:

select * from T1 where x in ( select y from T2 ) 
    
執行的過程至關於:
     select *  from t1, ( select distinct y from t2 ) t2 
 where t1.x = t2.y;

而使用exists
    select * from t1 where exists ( select null from t2 where y = x )  
執行的過程至關於:
for x in ( select * from t1 )
loop
   if ( exists ( select null from t2 where y = x.x )
   then 
      OUTPUT THE RECORD
   end if
end loop

in的方式比較直觀,exists則有些繞,並且in能夠用於各類子查詢,而exists好像
只用於關聯子查詢(其餘子查詢固然也能夠用,惋惜沒意義)。 因爲exists是用loop的
方式,因此,循環的次數對於exists影響最大,因此,外表要記錄數少,內表就無所謂了,
而in用的是hash join,因此內表若是小,整個查詢的範圍都會很小,若是內表很大,外表
若是也很大就很慢了,這時候exists才真正的會快過in的方式。        
   若是查詢語句使用了not in 那麼內外表都進行全表掃描,沒有用到索引;而not extsts
的子查詢依然能用到表上的索引。因此不管那個表大,用not exists都比not in要快。 也
就是說,in和exists須要具體狀況具體分析,not in和not exists就不用分析了,儘可能用
not exists就行了。

典型的鏈接類型共有3種:

排序 - - 合併鏈接(Sort Merge Join (SMJ) ) 
嵌套循環(Nested Loops (NL) ) 
哈希鏈接(Hash Join)

嵌套循環和哈希鏈接的算法仍是有不一樣,在理論上哈希鏈接要快過排序和nl,固然實際
狀況比理論上有複雜的多,不過二者仍是有差別的.

1 關聯子查詢與非關聯子查詢

關聯子查詢須要在內部引用外部表,而非關聯子查詢不要引用外部表。對於父查詢中處理
的記錄來講,一個關聯子查詢是每行計算一次,然而一個非關聯子查詢只會執行一次,並且結
果集被保存在內存中(若是結果集比較小),或者放在一張oracle臨時數據段中(若是結果集
比較大)。一個「標量」子查詢是一個非關聯子查詢,返回惟一記錄。若是子查詢僅僅返回一個
記錄,那麼oracle優化器會將結果縮減爲一個常量,並且這個子查詢只會執行一次。

   select from emp where deptno in (select deptno from dept where 
dept_name=’admin’);

2. 如何選擇?

根據外部查詢,以及子查詢自己所返回的記錄的數目。若是兩種查詢返回的結果是相同
 的,哪個效率更好?

     關聯子查詢的系統開銷:對於返回到外層查詢的記錄來講,子查詢會每次執行一次。因
  此,必須保證任何可能的時候子查詢都要使用索引。非關聯子查詢的系統開銷:子查詢只會
  執行一次,並且結果集一般是排好序的,並保存在臨時數據段中,其中每個記錄在返回時
  都會被父級查詢引用,在子查詢返回大量記錄的狀況下,將這些結果集排序回增大系統的開
  銷。

     因此:若是父查詢只返回較少的記錄,那麼再次執行子查詢的開銷不會很是大,若是返
 回不少數據行,那麼直查詢就會執行不少次。 若是子查詢返回較少的記錄,那麼爲內存中
 保存父查詢的結果集的系統開銷不會很是大,若是子查詢返回多行,那麼須要將結果放在臨
 時段上,而後對數據段排序,以便爲負查詢中的每一個記錄服務。

3.結論

  • 在使用一個關聯子查詢是,使用in 或者 exists子句的子查詢執行計劃一般都相同
  • exists子句一般不適於子查詢
  • 在外部查詢返回相對較少記錄時,關聯子查詢比非關聯子查詢執行得要更快。
  • 若是子查詢中只有少許的記錄,則非關聯子查詢會比關聯子查詢執行得更快。

4 子查詢轉化:子查詢能夠轉化爲標準鏈接操做

- 使用in的非關聯子查詢(子查詢惟一)
    條件:1.在整個層次結構中最底層數據表上定義惟一主鍵的數據列存在於子查詢的
            select列表中

          2.至少有個定義了惟一主鍵的數據列在select列表中,並且定義惟一主鍵的
            其餘數據列都必須有指定的相等標準,不論是直接指定,仍是間接指定。

- 使用exists子句的關聯子查詢

    條件:對於相關條件來講,該子查詢只能返回一個記錄。

5. not in和not exists調整

- not in 非關聯子查詢:轉化爲in寫法下的minus子句

   - not exists關聯子查詢:這種類型的反鏈接操做會爲外部查詢中每個記錄進行
     內部查詢,除了不知足子查詢中where條件的內部數據表之外,他會過濾掉全部記
     錄。

       能夠重寫:在一個等值鏈接中指定外部連接條件,而後添加
        select distinct ... from a,b where a.col1 = b.col1(+) 
        and b.col1 is null

6. 在子查詢中使用all any

原文地址
若是有侵權,立刻刪除算法

相關文章
相關標籤/搜索