您的位置:首页 > 数据库 > Oracle

关于Oracle中in和exists的区别

2015-12-17 16:55 513 查看
1、关于在 Oracle8i  时代中in和exists的区别

 

这里有条SQL语句:select * from A where id in(select id from B) 

以上查询使用了in语句,in()只执行一次,它查出B表中的所有id字段并缓存起来.之后,检查A表的id是否与B表中的id相等,如果相等则将A表的记录加入结果集中,直到遍历完A表的所有记录;

它的查询过程类似于以下过程

List resultSet=[];
Array A=(select * from A);
Array B=(select id from B);

for(int i=0;i<A.length;i++) {
   for(int j=0;j<B.length;j++) {
      if(A[i].id==B[j].id) {
         resultSet.add(A[i]);
         break;
      }
   }
}
return resultSet;

可以看出,当B表数据较大时不适合使用in(),因为它会B表数据全部遍历一次.

如:A表有10000条记录,B表有1000000条记录,那么最多有可能遍历10000*1000000次,效率很差.

再如:A表有10000条记录,B表有100条记录,那么最多有可能遍历10000*100次,遍历次数大大减少,效率大大提升.

结论1:in()适合B表比A表数据小的情况

这里还有一条SQL语句: select a.* from A a where exists(select 1 from B b where a.id=b.id)

以上查询使用了exists语句,exists()会执行A.length次,它并不缓存exists()结果集,因为exists()结果集的内容并不重要,重要的是结果集中是否有记录,如果有则返回true,没有则返回false.

它的查询过程类似于以下过程

 

List resultSet=[];
Array A=(select * from A)

 

for(int i=0;i<A.length;i++) {
   if(exists(A[i].id) {    //执行select 1 from B b where b.id=a.id是否有记录返回
       resultSet.add(A[i]);
   }
}
return resultSet;

结论2:exists()适合B表比A表数据大的情况

当B表比A表数据大时适合使用exists(),因为它没有那么遍历操作,只需要再执行一次查询就行.
如:A表有10000条记录,B表有1000000条记录,那么exists()会执行10000次去判断A表中的id是否与B表中的id相等.
如:A表有10000条记录,B表有100000000条记录,那么exists()还是执行10000次,因为它只执行A.length次,可见B表数据越多,越适合exists()发挥效果.
再如:A表有10000条记录,B表有100条记录,那么exists()还是执行10000次,还不如使用in()遍历10000*100次,因为in()是在内存里遍历比较,而exists()需要查询数据库,我们都知道查询数据库所消耗的性能更高,而内存比较很快.

当A表数据与B表数据一样大时,in与exists效率差不多,可任选一个使用.

In适合内外表都很大的情况,exists适合外表结果集很小的情况。

 

In和exists对比:

若子查询结果集比较小,优先使用in,若外层查询比子查询小,优先使

用exists。因为若用in,则oracle会优先查询子查询,然后匹配外层查询,

若使用exists,则oracle会优先查询外层表,然后再与内层表匹配。最优化

匹配原则,拿最小记录匹配大记录

 
2、[b]关于在 Oracle8i 之后 时代中in和exists的区别[/b]

in 是把外表和内表作hash join,而exists是对外表作loop,每次loop再对内表进行查询。

一直以来认为exists比in效率高的说法是不准确的。

如果查询的两个表大小相当,那么用in和exists差别不大。

如果两个表中一个较小,一个是大表,则子查询表大的用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列的索引。

带in的关联子查询是多余的,因为in子句和子查询中相关的操作的功能是一样的。如:

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);

 

为非关联子查询指定exists子句是不适当的,因为这样会产生笛卡乘积。如: 

select staff_name from staff_member where staff_id 

exists (select staff_id from staff_func);

not in 和not exists

如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;

而not extsts 的子查询依然能用到表上的索引。

所以无论哪个表大,用not exists都比not in要快。

尽量不要使用not in子句。使用minus 子句都比not in 子句快,虽然使用minus子句要进行两次查询: 

select staff_name from staff_member where staff_id in (select staff_id from staff_member minus select staff_id from staff_func where func_id like '81%');

in 与 "=" 的区别

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



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

的结果是相同的。

 

 
3、关于在 Oracle8i 之后 时代中in和exists的区别


    在ORACLE 11G大行其道的今天,还有很多人受早期版本的影响,记住一些既定的规则,
   1.子查询结果集小,用IN

   2.外表小,子查询表大,用EXISTS


这是完全错误的观点。在8i时代,这经常是正确的,但是现在已经11G了,马上12C就要面世了。其实在ORACLE 9i CBO就已经优化了IN,EXISTS的区别,ORACLE优化器有个查询转换器,很多SQL虽然写法不同,但是ORACLE优化器会根据既定规则进行查询重写,重写为优化器觉得效率最高的SQL,所以可能SQL写法不同,但是执行计划却是完全一样的。

 [b]IN与EXISTS有一点要记住:IN一般是用于非相关子查询,而EXISTS一般用于相关子查询。当然IN也可以用于相关子查询,EXISTS也可以用于非相关子查询。但是这区别很重要,虽然优化器很强大,但是查询转换是有一定的限制的,在EXISTS性能低下,无法进行相关查询转换,比如不能UNNEST
SUBQUERY,那么可能我们需要改写SQL,通常可以用IN/JOIN等改写相关子查询,或对于含有OR的SEMI JOIN[b]改
为UNION ALL/UNION的形式。[/b][/b]

下面就用例子说明一下:

DROP TABLE a;

DROP TABLE b;

CREATE TABLE a AS SELECT * FROM hr.employees;

CREATE TABLE b AS SELECT * FROM  hr.employees;

--反复插入,构造20万行+

INSERT INTO a SELECT * FROM a;

INSERT INTO b SELECT * FROM a;

COMMIT;

BEGIN

 dbms_stats.gather_table_stats(ownname => USER,tabname => 'a',estimate_percent => 100,cascade => TRUE);

  dbms_stats.gather_table_stats(ownname => USER,tabname => 'b',estimate_percent => 100,cascade => TRUE);

END;

/
1.测试IN,EXISTS在简单查询中,是等价的

SQL> set autotrace traceonly exp

SQL> SELECT * FROM a

  2  WHERE EXISTS(

  3  SELECT 1 FROM b WHERE a.employee_id=b.employee_id);

执行计划

----------------------------------------------------------

Plan hash value: 2317816356

---------------------------------------------------------------------------------

| Id  | Operation              | Name   | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------

|   0 | SELECT STATEMENT       |        |   217K|    15M|  1375   (2)| 00:00:17 |

|*  1 |  HASH JOIN             |        |   217K|    15M|  1375   (2)| 00:00:17 |

|   2 |   SORT UNIQUE          |        |   217K|   848K|   126   (1)| 00:00:02 |

|   3 |    INDEX FAST FULL SCAN | IDX2_B |   217K|   848K|   126   (1)| 00:00:02 |

|   4 |   TABLE ACCESS FULL    | A      |   217K|    14M|   620   (1)| 00:00:08 |

---------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   1 - access("A"."EMPLOYEE_ID"="B"."EMPLOYEE_ID")

SQL> SELECT * FROM a

  2  WHERE a.employee_id IN (SELECT b.employee_id FROM b);

执行计划

----------------------------------------------------------

Plan hash value: 2317816356

---------------------------------------------------------------------------------

| Id  | Operation            | Name   | Rows   | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------

|   0 | SELECT STATEMENT       |        |   217K |   15M|  1375   (2) | 00:00:17 |

|*  1 |  HASH JOIN             |        |   217K |   15M|  1375   (2) | 00:00:17 |

|   2 |   SORT UNIQUE        |        |   217K |  848K|   126   (1) | 00:00:02 |

|   3 |    INDEX FAST FULL SCAN | IDX2_B |   217K |  848K|   126   (1) | 00:00:02 |

|   4 |   TABLE ACCESS FULL     | A       |   217K |  14M|   620   (1) | 00:00:08 |

---------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   1 - access("A"."EMPLOYEE_ID"="B"."EMPLOYEE_ID")
 


  可以看出,两个计划完全没有区别。类似于ORACLE查询重写为:

 SELECT a.* FROM a,(SELECT DISTINCT b.employee_id FROM b) b1 WHERE a.employee_id=b1.employee_id
ac51
;


 

 

 结语:讨论SQL语句:select * from A where id in (1,2,3,4,5,6,7,8,9) 中 在Oracle8i 版本之后的Oracle数据库中性能是一样的。

 

尊重原创:

                  Oracle8i 原创理论转自百度空间

     Oracle8i之后版本理论出处:

                  http://trailblizer.blog.163.com/blog/static/59630364201024101539938/

                  http://blog.chinaunix.net/uid-7655508-id-3626647.html 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Oracle