Oracle数据库中经常会在子查询中使用in和exists语句,在sql优化中,也经常会遇到将in子句改成exists子句的情况。只有搞清楚这两个语句的执行过程才能在正确使用它们,下面分别介绍。
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、执行过程
2.1 in子句
SELECT *
FROM T1
WHERE x IN
(SELECT y
FROM T2)
执行过程大概相当于表连接:
select *
from t1, ( select distinct y from t2 ) t2
where t1.x = t2.y;
大概来说,in子句中的子查询会先被解析、执行,然后,得到结果集,然后去重。最后,该结果集和原来的t1表做连接,最后得到查询结构。
2.2 exists子句
SELECT *
FROM t1
WHERE exists
(SELECT 'CONST'
FROM t2
WHERE y = x )
大概的执行过程,相当于一个外层查询结果的嵌套循环:
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
上面的执行过程,大概是,对于t1表全表扫描之后得到的每一条记录,执行exists子句中的子查询,从而得到最后的结果集。
3、适用场景
具体的适用场景大概要从两个方面考虑:T2表的数据量和T1表的数据量。
3.1 T2表的数据量
从原理上可以看出,如果T2表中的记录非常少,子查询( select y from T2 )
执行的时间特别短,这时候更适合用in子句。
相反,如果T2表中的记录非常多,子查询( select y from T2 )
执行的时间特别长,这时候显然不适合用in子句,而更加适合用exists子句。这样,能节省掉对T2表进行全表扫描得到查询结果,然后去重的时间。除此之外,为了提高子查询的执行速度,我们经常会在T2(y)上建立索引。实际上,这是一个在sql效率优化中特别常用的一个小技巧。
3.2 T1表的数据量
同样的,如果T1表的数据量特别大,这时候,如果采用exists子句的写法,外层循环的次数会特别多,这样会导致查询耗时增多。所以,这时候,用in子句可能会更好,in子句在执行时可能会被oracle优化成一个连接,优化器会针对这个连接做一些优化来提高效率。
当然,如果T1表和T2表的数据量都很大,那这两种写法的效率就差不多了。这时候,具体的执行效率就要取决于表上的索引或者其他因素了。
其实,这里的T1和T2只是为了表述的方便,实际的sql优化中,这里多半是一个表通过各种where条件筛选后的查询结果。
参考链接:
https://stackoverflow.com/questions/12896007/oracle-in-vs-exists-difference