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 wherecc=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
5
使用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適合於外表小而內表大的情況。
我們要根據實際的情況做相應的優化,不能絕對的說誰的效率高誰的效率低,所有的事都是相對的