oracle中in,not in和exists,not exists之间的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle中in,not in和exists,not exists之间的区别相关的知识,希望对你有一定的参考价值。

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、关于在 Oracle8i 之后 时代中in和exists的区别
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写法不同,但是执行计划却是完全一样的。
IN与EXISTS有一点要记住:IN一般是用于非相关子查询,而EXISTS一般用于相关子查询。当然IN也可以用于相关子查询,EXISTS也可以用于非相关子查询。但是这区别很重要,虽然优化器很强大,但是查询转换是有一定的限制的,在EXISTS性能低下,无法进行相关查询转换,比如不能UNNEST SUBQUERY,那么可能我们需要改写SQL,通常可以用IN/JOIN等改写相关子查询,或对于含有OR的SEMI JOIN改为UNION ALL/UNION的形式。
下面就用例子说明一下:

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;

结语:讨论SQL语句:select * from A where id in (1,2,3,4,5,6,7,8,9) 中 在Oracle8i 版本之后的Oracle数据库中性能是一样的。
参考技术A 如果in的数据量少,比exists更有效率;如果in的数量大,则exists更有效率

本回答被提问者采纳

oracle_not exists和not in的用法和区别

(转 飞翔-方向 积累 沉淀http://www.cnblogs.com/mytechblog/)

sql中exists,not exists的用法

exists表示()内子查询语句返回结果不为空说明where条件成立就会执行主sql语句,如果为空就表示where条件不成立,sql语句就不会执行。not existsexists相反,子查询语句结果为空,则表示where条件成立,执行sql语句。否则不执行。

exists : 强调的是是否返回结果集,不要求知道返回什么, 比如:
  select name from student where sex = \'m\' and mark exists(select 1 from grade where ...) ,只要
exists引导的子句有结果集返回,那么exists这个条件就算成立了,大家注意返回的字段始终为1,如果改成“select 2 from grade where ...”,那么返回的字段就是2,这个数字没有意义。所以exists子句不在乎返回什么,而是在乎是不是有结果集返回。

而 exists 与 in 最大的区别在于 in引导的子句只能返回一个字段,比如:
  select name from student where sex = \'m\' and mark in (select 1,2,3 from grade where ...)  
,in子句返回了三个字段,这是不正确的,exists子句是允许的,但in只允许有一个字段返回,在1,2,3中随便去了两个字段即可。

not exists 和not in 分别是exists 和 in 的 对立面。

exists (sql 返回结果集为真)  
not exists (sql 不返回结果集为真)

下面详细描述not exists的过程:

如下:
表A
ID NAME  
1   A1
2   A2
3   A3

表B
ID AID NAME
1   1     B1
2   2     B2  
3   2     B3

表A和表B是1对多的关系 A.ID => B.AID

SELECT ID,NAME FROM A WHERE EXISTS (SELECT * FROM B WHERE A.ID=B.AID)
执行结果为
1 A1
2 A2
原因可以按照如下分析
SELECT ID,NAME FROM A WHERE EXISTS (SELECT * FROM B WHERE B.AID=1)
--->SELECT * FROM B WHERE B.AID=1有值返回真所以有数据

SELECT ID,NAME FROM A WHERE EXISTS (SELECT * FROM B WHERE B.AID=2)
--->SELECT * FROM B WHERE B.AID=2有值返回真所以有数据

SELECT ID,NAME FROM A WHERE EXISTS (SELECT * FROM B WHERE B.AID=3)
--->SELECT * FROM B WHERE B.AID=3无值返回真所以没有数据

NOT EXISTS 就是反过来
SELECT ID,NAME FROM A WHERE NOT EXIST (SELECT * FROM B WHERE A.ID=B.AID)
执行结果为
3 A3
===========================================================================

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

EXISTS = IN,意思相同不过语法上有点点区别,好像使用IN效率要差点,应该是不会执行索引的原因
SELECT ID,NAME FROM A  WHERE ID IN (SELECT AID FROM B)

NOT EXISTS = NOT IN ,意思相同不过语法上有点点区别
SELECT ID,NAME FROM A WHERE ID NOT IN (SELECT AID FROM B)

有时候我们会遇到要选出某一列不重复,某一列作为选择条件,其他列正常输出的情况.

如下面的表table:

Id  Name  Class Count  Date

 1   苹果    水果    10     2011-7-1

 1   桔子    水果    20     2011-7-2

 1   香蕉    水果    15     2011-7-3

 2   白菜    蔬菜    12     2011-7-1

 2   青菜    蔬菜    19     2011-7-2

如果想要得到下面的结果:(Id唯一,Date选最近的一次)

1   香蕉    水果    15     2011-7-3

2   青菜    蔬菜    19     2011-7-2

正确的SQL语句是:

SELECT Id, Name, Class, Count, Date
FROM table t
WHERE (NOT EXISTS
          (SELECT Id, Name, Class, Count, Date FROM table 
         WHERE Id = t.Id AND Date > t.Date))

如果用distinct,得不到这个结果, 因为distinct是作用与所有列的

SELECT DISTINCT Id, Name, Class, Count, Date FROM table

结果是表table的所有不同列都显示出来,如下所示:

 1   苹果     水果    10     2011-7-1

 1   桔子    水果    20     2011-7-2

 1   香蕉    水果    15     2011-7-3

 2   白菜    蔬菜    12     2011-7-1

 2   青菜    蔬菜    19     2011-7-2

如果用Group by也得不到需要的结果,因为Group by 要和聚合函数共同使用,所以对于Name,Class和Count列要么使用Group by,要么使用聚合函数. 如果写成

SELECT Id, Name, Class, Count, MAX(Date) 
FROM table
GROUP BY Id, Name, Class, Count

得到的结果是

 1   苹果     水果    10    2011-7-1

 1   桔子    水果    20     2011-7-2

 1   香蕉    水果    15     2011-7-3

 2   白菜    蔬菜    12     2011-7-1

 2   青菜    蔬菜    19     2011-7-2

如果写成

SELECT Id, MAX(Name), MAX(Class), MAX(Count), MAX(Date) 
FROM table 
GROUP BY Id

得到的结果是:

 1   香蕉    水果    20     2011-7-3

 2   青菜    蔬菜    19     2011-7-2

如果用in有时候也得不到结果,(有的时候可以得到,如果Date都不相同(没有重复数据),或者是下面得到的Max(Date)只有一个值)

SELECT DISTINCT Id, Name, Class, Count, Date FROM table

WHERE (Date IN
          (SELECT MAX(Date)
         FROM table
         GROUP BY Id))

得到的结果是:(因为MAX(Date)有两个值2011-7-2,2011-7-3)

 1   桔子    水果    20     2011-7-2

 1   香蕉    水果    15     2011-7-3

 2   青菜    蔬菜    19     2011-7-2

注意in只允许有一个字段返回

有一种方法可以实现:

SELECT Id, Name, Class, COUNT, Date
FROM table1 t
WHERE (Date =
          (SELECT MAX(Date)
         FROM table1
         WHERE Id = t .Id))

 

以上是关于oracle中in,not in和exists,not exists之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

Oracle中 inexistsnot in,not exists的比较

Oracle,用left join 替代 exists ,not exists,in , not in,提高效率

Oracle-where exists()not exists() in()not in()用法以及效率差异

转 [ORACLE]详解not in与not exists的区别与用法(not in的性能并不差!)

(008)每日SQL学习:Oracle Not Exists 及 Not In 使用

Oracle in与exists语句