1. 程式人生 > >Oracle 條件語句 in和exists 區別

Oracle 條件語句 in和exists 區別

1、in和exists

in是把外表和內表作hash連線,而exists是對外表作loop迴圈,每次loop迴圈再對內表進行查詢,一直以來認為exists比in效率高的說法是不準確的。如果查詢的兩個表大小相當,那麼用in和exists差別不大;如果兩個表中一個較小一個較大,則子查詢表大的用exists,子查詢表小的用in;

例如:表A(小表),表B(大表)

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列的索引。

相反的:

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列的索引。

2、not in 和not exists

not in 邏輯上不完全等同於not exists,如果你誤用了not in,小心你的程式存在致命的BUG,請看下面的例子:

複製程式碼
create table #t1(c1 int
,c2 int); create table #t2(c1 int,c2 int); insert into #t1 values(1,2); insert into #t1 values(1,3); insert into #t2 values(1,2); insert into #t2 values(1,null); select * from #t1 where c2 not in(select c2 from #t2);  -->執行結果:無 select * from #t1 where not exists(select 1 from #t2 where #t2.c2=
#t1.c2)  -->執行結果:1  3
複製程式碼

正如所看到的,not in出現了不期望的結果集,存在邏輯錯誤。如果看一下上述兩個select 語句的執行計劃,也會不同,後者使用了hash_aj,所以,請儘量不要使用not in(它會呼叫子查詢),而儘量使用not exists(它會呼叫關聯子查詢)。如果子查詢中返回的任意一條記錄含有空值,則查詢將不返回任何記錄。如果子查詢欄位有非空限制,這時可以使用not in,並且可以通過提示讓它用hasg_aj或merge_aj連線。

如果查詢語句使用了not in,那麼對內外表都進行全表掃描,沒有用到索引;而not exists的子查詢依然能用到表上的索引。所以無論哪個表大,用not exists都比not in 要快。

3、in 與 = 的區別

select name from student where name in('zhang','wang','zhao');

select name from student where name='zhang' or name='wang' or name='zhao'

的結果是相同的。

-----

其他分析:

1.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。 
其實我們區分in和exists主要是造成了驅動順序的改變(這是效能變化的關鍵),如果是exists,那麼以外層表為驅動表,先被訪問,如果是IN,那麼先執行子查詢,所以我們會以驅動表的快速返回為目標,那麼就會考慮到索引及結果集的關係了 


另外IN時不對NULL進行處理 
如: select 1 from dual where null in (0,1,2,null) 為空 


2.NOT IN 與NOT EXISTS: 
NOT EXISTS的執行流程 
select ..... from rollup R  where not exists ( select 'Found' from title T where R.source_id = T.Title_ID); 
可以理解為: 
for x in ( select * from rollup ) loop 
if ( not exists ( that query ) ) then 
OUTPUT 
end if; 
end loop; 


注意:NOT EXISTS 與 NOT IN 不能完全互相替換,看具體的需求。如果選擇的列可以為空,則不能被替換。 


例如下面語句,看他們的區別: 
select x,y from t; 

查詢x和y資料如下:
x y 
------ ------ 
1 3 
3 1 
1 2 
1 1 
3 1 

使用not in 和not exists查詢結果如下:
select * from t where x not in (select y from t t2 ) ;
查詢無結果:no rows


select * from t where not exists (select null from t t2 where t2.y=t.x ) ;

查詢結果為:
x y 
------ ------ 
5 NULL 


所以要具體需求來決定 


對於not in 和 not exists的效能區別: 
not in 只有當子查詢中,select 關鍵字後的欄位有not null約束或者有這種暗示時用not in,另外如果主查詢中表大,子查詢中的表小但是記錄多,則應當使用not in,並使用anti hash join. 
如果主查詢表中記錄少,子查詢表中記錄多,並有索引,可以使用not exists,另外not in最好也可以用/*+ HASH_AJ */或者外連線+is null 
NOT IN 在基於成本的應用中較好 


比如: 
select ..... 
from rollup R 
where not exists ( select 'Found' from title T 
where R.source_id = T.Title_ID); 


改成(佳) 


select ...... 
from title T, rollup R 
where R.source_id = T.Title_id(+) 
and T.Title_id is null; 


或者(佳) 
sql> select /*+ HASH_AJ */ ... 
from rollup R 
where ource_id NOT IN ( select ource_id 
from title T 
where ource_id IS NOT NULL ) 


討論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; 
——如果你有一定的SQL優化經驗,從這句很自然的可以想到t2絕對不能是個大表,因為需要對t2進行全表的“唯一排序”,如果t2很大這個排序的效能是 不可忍受的。但是t1可以很大,為什麼呢?最通俗的理解就是因為t1.x=t2.y可以走索引。但這並不是一個很好的解釋。試想,如果t1.x和t2.y 都有索引,我們知道索引是種有序的結構,因此t1和t2之間最佳的方案是走merge join。另外,如果t2.y上有索引,對t2的排序效能也有很大提高。 
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 
——這個更容易理解,t1永遠是個表掃描!因此t1絕對不能是個大表,而t2可以很大,因為y=x.x可以走t2.y的索引。 
綜合以上對IN/EXISTS的討論,我們可以得出一個基本通用的結論:IN適合於外表大而內表小的情況;EXISTS適合於外表小而內表大的情況。 
我們要根據實際的情況做相應的優化,不能絕對的說誰的效率高誰的效率低,所有的事都是相對的