大神级回答exists与in的区别

google搜了一下,很多帖子,而且出发点不同,各有各的道理,但是有一个帖子讲的特别好:

http://zhidao.baidu.com/question/134174568.html

忍不住在百度上回复了一下,怒赞,没想到别人早就回复过了:围观大神级回答。确实名副其实!

================in和exists=============================
in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。

如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:
例如:表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要快。

------------------------我的评论-----------------------------------------

1本质上是要利用索引,2要区分大表小表,3not exists子查询能使用索引,速度更快。我的真实SQL是这样的:

INSERT INTO journal_curr
SELECT '2008-10-01' AS date_imp, act_numero, act_sexe, titre, act_nom, act_nom_abrege, act_sous_titre,act_adr_numero,act_adr_rue,
ltrim(concat( IFNULL(act_adr_numero,''),' ',act_adr_rue)) AS act_adr,act_adr_complement,act_cp,LEFT(act_cp,2) AS region,act_ville,act_pays, nb_exempl
FROM journal_abonn INNER JOIN actif
ON (journal_abonn.ref_client = actif.act_numero)
AND (ref_journal='NOUVEURO')
AND (act_pays<>'ALLEMAGNE')
AND ( ((date_abonn<='2008-10-01') AND (date_fin_abonn>='2008-10-01') AND (date_abonn_fin_envoi IS NULL))
OR ((date_abonn<='2008-10-01') AND (date_abonn_fin_envoi>='2008-10-01'))
OR ((act_type_abonne=4) AND ((date_fin_abonn>='2008-10-01') AND (date_abonn_fin_envoi IS NULL)))
OR ((act_type_abonne=4) AND (date_fin_abonn IS NULL) AND (date_abonn_fin_envoi IS NULL))
OR ((act_type_abonne=4) AND ((date_abonn_fin_envoi>='2008-10-01')))
)
AND (ref_abonn_new NOT IN (SELECT ref_abonn_new FROM journal_suspension
WHERE (debut_suspension<='2008-10-01')
AND (fin_suspension>='2008-10-01')
AND (ref_journal='NOUVEURO')
)
)

自从给journal_abonn加了ref_client加了索引以后,时间从30分钟下降到几秒(注意,我没给ref_abonn_new加索引)。
有趣的是,我这个例子刚好journal_abonn是大表,journal_suspension是小表,符合IN的使用条件。
大表真正使用的关键索引是ref_client,而不是ref_abonn_new,所以可以充分利用上。
小表计算速度无所谓,而且根据研究小表应该也是一次计算完后放在内存中。
另外,我这里是NOT IN,但是速度仍然很快,说明仍然利用上了索引,难道大神级回复也有小错?

================================================================

另外,我重新整理一下所有我搜到的发言,都很有道理:

通过使用exist,oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间(我的评论,主表小当然应该先过滤主查询)。oracle系统在执行in子查询时,首先执行子查询(我的评论,从表小当然应该先过滤子查询),并将获得的结果列表存放在在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用exists比使用in通常查询速度快的原因。

下面是一个非关联子查询: 
  select staff_name from staff_member where staff_id 
  in (select staff_id from staff_func); 
  而下面是一个关联子查询: 
  select staff_name from staff_member where staff_id in (select staff_id from staff_func where staff_member.staff_id=staff_func.staff_id); 
  以上返回的结果集是相同的,可是它们的执行开销是不同的: 
  非关联查询的开销——非关联查询时子查询只会执行一次,而且结果是排序好的,并保存在一个ORACLE的临时段中,其中的每一个记录在返回时都会被父查询所引用。在子查询返回大量的记录的情况下,将这些结果集排序,以及将临时数据段进行排序会增加大量的系统开销。 
  关联查询的开销——对返回到父查询的的记录来说,子查询会每行执行一次。因此,我们必须保证任何可能的时候子查询用到索引。 

楼上说法片面,in和exist,各有快的时候,主要是看你的筛选条件是在主查询上还是在子查询上
下面是oracle文档,:)

In certain circumstances, it is better to use IN rather than EXISTS. In general, if the selective predicate is in the subquery, then use IN. If the selective predicate is in the parent query, then use EXISTS.

Sometimes, Oracle can rewrite a subquery when used with an IN clause to take advantage of selectivity specified in the subquery. This is most beneficial when the most selective filter appears in the subquery and there are indexes on the join columns. Conversely, using EXISTS is beneficial when the most selective filter is in the parent query. This allows the selective predicates in the parent query to be applied before filtering the rows against the EXISTS criteria.

exists不需要记录,当存在的时候就返回。
用exists只检查行的存在性,而in检查到行里的实际的值。

参考:
http://bbs.csdn.net/topics/190124638
http://blog.csdn.net/lick4050312/article/details/4476333

A,B两个表
当只显示一个表的数据如A,关系条件只一个如ID时,使用IN更快:select * from A where id in (select id from B)
当只显示一个表的数据如A,关系条件不只一个如ID,col1时,使用IN就不方便了,可以使用EXISTS:select * from A where exists (select 1 from B where id = A.id and col1 = A.col1)
当只显示两个表的数据时,使用IN,EXISTS都不合适,要使用连接:select * from A left join B on id = A.id

参考:

http://www.cnblogs.com/AllUserBegin/p/3513084.html

原文地址:https://www.cnblogs.com/findumars/p/3491378.html