Oracle數(shù)據(jù)庫中經(jīng)常會(huì)在子查詢中使用in和exists語句,在sql優(yōu)化中,也經(jīng)常會(huì)遇到將in子句改成exists子句的情況。只有搞清楚這兩個(gè)語句的執(zhí)行過程才能在正確使用它們,下面分別介紹。
1、語法
in語句:
SELECT *
FROM T1
WHERE x IN
(SELECT y
FROM T2)
exists語句:
SELECT *
FROM t1
WHERE exists
(SELECT 'CONST'
FROM t2
WHERE y = x )
2、執(zhí)行過程
2.1 in子句
SELECT *
FROM T1
WHERE x IN
(SELECT y
FROM T2)
執(zhí)行過程大概相當(dāng)于表連接:
select *
from t1, ( select distinct y from t2 ) t2
where t1.x = t2.y;
大概來說,in子句中的子查詢會(huì)先被解析、執(zhí)行,然后,得到結(jié)果集,然后去重。最后,該結(jié)果集和原來的t1表做連接,最后得到查詢結(jié)構(gòu)。
2.2 exists子句
SELECT *
FROM t1
WHERE exists
(SELECT 'CONST'
FROM t2
WHERE y = x )
大概的執(zhí)行過程,相當(dāng)于一個(gè)外層查詢結(jié)果的嵌套循環(huán):
for x in ( select * from t1 )
loop
if ( exists ( select ‘CONST’ from t2 where y = x.x )
then
OUTPUT THE RECORD
end if
end loop
上面的執(zhí)行過程,大概是,對于t1表全表掃描之后得到的每一條記錄,執(zhí)行exists子句中的子查詢,從而得到最后的結(jié)果集。
3、適用場景
具體的適用場景大概要從兩個(gè)方面考慮:T2表的數(shù)據(jù)量和T1表的數(shù)據(jù)量。
3.1 T2表的數(shù)據(jù)量
從原理上可以看出,如果T2表中的記錄非常少,子查詢( select y from T2 )執(zhí)行的時(shí)間特別短,這時(shí)候更適合用in子句。
相反,如果T2表中的記錄非常多,子查詢( select y from T2 )執(zhí)行的時(shí)間特別長,這時(shí)候顯然不適合用in子句,而更加適合用exists子句。這樣,能節(jié)省掉對T2表進(jìn)行全表掃描得到查詢結(jié)果,然后去重的時(shí)間。除此之外,為了提高子查詢的執(zhí)行速度,我們經(jīng)常會(huì)在T2(y)上建立索引。實(shí)際上,這是一個(gè)在sql效率優(yōu)化中特別常用的一個(gè)小技巧。
3.2 T1表的數(shù)據(jù)量
同樣的,如果T1表的數(shù)據(jù)量特別大,這時(shí)候,如果采用exists子句的寫法,外層循環(huán)的次數(shù)會(huì)特別多,這樣會(huì)導(dǎo)致查詢耗時(shí)增多。所以,這時(shí)候,用in子句可能會(huì)更好,in子句在執(zhí)行時(shí)可能會(huì)被oracle優(yōu)化成一個(gè)連接,優(yōu)化器會(huì)針對這個(gè)連接做一些優(yōu)化來提高效率。
當(dāng)然,如果T1表和T2表的數(shù)據(jù)量都很大,那這兩種寫法的效率就差不多了。這時(shí)候,具體的執(zhí)行效率就要取決于表上的索引或者其他因素了。
其實(shí),這里的T1和T2只是為了表述的方便,實(shí)際的sql優(yōu)化中,這里多半是一個(gè)表通過各種where條件篩選后的查詢結(jié)果。
參考鏈接:
https://stackoverflow.com/questions/12896007/oracle-in-vs-exists-difference