在网上搜了下关于oracle中not

exists和not

in性能的比较,发现没有描述的太全面的,可能是问题太简单了,达人们都不屑于解释吧。于是自己花了点时间,试图把这个问题简单描述清楚,其实归根结底一句话:not in性能并不比not exists差,关键看你用的是否正确。

我先建两个示范表,便于说明:

create table ljn_test1 (col

number);

create table ljn_test2 (col

number);

然后插入一些数据:

insert into ljn_test1

select level from dual connect by

level <=30000;

insert into ljn_test2

select level+1 from dual connect by

level <=30000;

commit;

然后来分别看一下使用not

exists和not

in的性能差异:

select * from ljn_test1 where not

exists (select 1 from ljn_test2 where ljn_test1.col =

ljn_test2.col);

COL

----------

1

Elapsed: 00:00:00.06

select * from ljn_test1 where col not

in (select col from ljn_test2);

COL

----------

1

Elapsed: 00:00:21.28

可以看到,使用not

exists需要0.06秒,而使用not

in需要21秒,差了3个数量级!为什么呢?其实答案很简答,以上两个SQL其实并不是等价的。

我把以上两个表的数据清除掉,重新插入数据:

truncate table ljn_test1;

truncate table ljn_test2;

insert into ljn_test1

values(1);

insert into ljn_test1

values(2);

insert into ljn_test1

values(3);

insert into ljn_test2

values(2);

insert into ljn_test2

values(null);

commit;

然后再次执行两个SQL:

select * from ljn_test1 where not

exists (select 1 from ljn_test2 where ljn_test1.col =

ljn_test2.col);

COL

----------

3

1

select * from ljn_test1 where col not

in (select col from ljn_test2);

no rows selected

这回not

in的原形暴露了,竟然得到的是空集。来仔细分解一下原因:

A. select * from

ljn_test1 where col not in (select col from ljn_test2);

A在这个例子中可以转化为下面的B:

B. select * from

ljn_test1 where col not in (2,null);

B可以进一步转化为下面的C:

C. select * from

ljn_test1 where col <> 2 and col

<> null;

因为col

<>

null是一个永假式,所以最终查出的结果肯定也就是空了。

由此可以得出结论:只要not

in的子查询中包含空值,那么最终的结果就为空!

not exists语句不会出现这种情况,因为not exists子句中写的是ljn_test1与ljn_test2的关联,null是不参与等值关联的,所以ljn_test2的col存在空值对最终的查询结果没有任何影响。

我在这里暂且把ljn_test1叫做外表,ljn_test2叫做内表。

只要稍做归纳,就可以得到更详细的结论:

1、对于not exists查询,内表存在空值对查询结果没有影响;对于not

in查询,内表存在空值将导致最终的查询结果为空。

2、对于not

exists查询,外表存在空值,存在空值的那条记录最终会输出;对于not

in查询,外表存在空值,存在空值的那条记录最终将被过滤,其他数据不受影响。

讲到这里,我就可以开始解释为什么上面的not

in语句比not

exists语句效率差这么多了。

not exists语句很显然就是一个简单的两表关联,内表与外表中存在空值本身就不参与关联,在CBO(基于成本的优化器)中常用的执行计划是hash

join,所以它的效率完全没有问题,看一下它的执行计划:

set autot on;

select * from ljn_test1 where not

exists (select 1 from ljn_test2 where ljn_test1.col =

ljn_test2.col);

COL

----------

3

1

Elapsed: 00:00:00.01

Execution Plan

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

Plan hash value: 385135874

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

| Id |

Operation

| Name

| Rows |

Bytes | Cost (%CPU)| Time

|

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

| 0 |

SELECT STATEMENT

|

|

3 |

78 |

7 (15)|

00:00:01 |

|* 1 | HASH JOIN

ANTI

|

|

3 |

78 |

7 (15)|

00:00:01 |

| 2

|

TABLE ACCESS FULL| LJN_TEST1 |

3 |

39 |

3

(0)| 00:00:01 |

| 3

|

TABLE ACCESS FULL| LJN_TEST2 |

2 |

26 |

3

(0)| 00:00:01 |

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

Predicate Information (identified by

operation id):

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

1 -

access("LJN_TEST1"."COL"="LJN_TEST2"."COL")

这个执行计划很清晰,没有什么需要解释的,再看一下not

in:

select * from ljn_test1 where col not

in (select col from ljn_test2);

no rows selected

Elapsed: 00:00:00.01

Execution Plan

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

Plan hash value:

3267714838

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

| Id |

Operation

| Name

| Rows |

Bytes | Cost (%CPU)| Time

|

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

| 0 |

SELECT STATEMENT

|

|

1 |

13 |

5

(0)| 00:00:01 |

|* 1 | FILTER

|

|

|

|

|

|

| 2

|

TABLE ACCESS FULL| LJN_TEST1 |

3 |

39 |

3

(0)| 00:00:01 |

|* 3 |

TABLE ACCESS FULL| LJN_TEST2 |

2 |

26 |

2

(0)| 00:00:01 |

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

Predicate Information (identified by

operation id):

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

1 -

filter( NOT EXISTS (SELECT 0 FROM "LJN_TEST2"

"LJN_TEST2"

WHERE LNNVL("COL"<>:B1)))

3 -

filter(LNNVL("COL"<>:B1))

可以看到关联谓词是filter,它类似于两表关联中的nested loop,也就是跑两层循环,可见它的效率有多差。为什么not in不能使用hash

join作为执行计划呢?正如上面解释的,因为内表或外表中存在空值对最终结果产生的影响是hash join无法实现的,因为hash join不支持把空值放到hash桶中,所以它没办法处理外表和内表中存在的空值,效率与正确性放在一起时,肯定是要选择正确性,所以oracle必须放弃效率,保证正确性,采用filter谓词。

这个执行计划中我们还有感兴趣的东西,那就是:LNNVL("COL"<>:B1),关于LNNVL的解释可以参见官方文档:http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions078.htm

它在这里的作用很巧妙,oracle知道使用filter性能很差,所以它在扫描内表ljn_test2时,会使用LNNVL来检查ljn_test2.col是否存在null值,只要扫描到null值,就可以断定最终的结果为空值,也就没有了继续执行的意义,所以oracle可以马上终止执行,在某种意义上它弥补了filter较差的性能。

我用例子来证明这一点,首先先造一些数据:

truncate table ljn_test1;

truncate table ljn_test2;

insert into ljn_test1

select level from dual connect by

level <=30000;

insert into ljn_test2

select level+1 from dual connect by

level <=30000;

commit;

然后我为了让oracle尽快扫描到ljn_test2.col为null的那条记录,我要先找到物理地址最小的那条记录,因为通常情况全表扫描会先扫描物理地址最小的那条记录:

select col from ljn_test2 where

rowid=(select min(rowid) from ljn_test2);

COL

----------

1982

然后我把这条记录更新为空:

update ljn_test2 set col = null where

col=1982;

commit;

然后再来看一下not

in的查询效率:

select * from ljn_test1 where col not

in (select col from ljn_test2);

no rows selected

Elapsed: 00:00:00.17

看到这个结果后我很爽,它和之前查询需要用时21秒有很大的差别!

当然,我们不能总是指望oracle扫描表时总是最先找到null值,看下面的例子:

update ljn_test2 set col = 1982 where

col is null;

select col from ljn_test2 where

rowid=(select max(rowid) from ljn_test2);

COL

----------

30001

update ljn_test2 set col = null where

col=30001;

commit;

再看一下not

in的查询效率:

select * from ljn_test1 where col not

in (select col from ljn_test2);

COL

----------

1

Elapsed: 00:00:21.11

这一下not

in再一次原形毕露了!

机会主义不行,更杯具的是如果内表中没有空值,那LNNVL优化就永远起不到作用,相反它还会增大开销!

其实只要找到原因,问题很好解决,不就是空值在作怪嘛!在正常的逻辑下用户本来就是想得到和not exists等价的查询结果,所以只要让oracle知道我们不需要空值参与进来就可以了。

第一种解决方案:

将内表与外表的关联字段设定为非空的:

alter table ljn_test1 modify col not

null;

alter table ljn_test2 modify col not

null;

好了,再看一下执行计划:

set autot on;

select * from ljn_test1 where col not

in (select col from ljn_test2);

COL

----------

1

Elapsed: 00:00:00.07

Execution Plan

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

Plan hash value: 385135874

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

| Id |

Operation

| Name

| Rows |

Bytes | Cost (%CPU)| Time

|

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

| 0 |

SELECT STATEMENT

|

|

1 |

26 |

28

(8)| 00:00:01 |

|* 1 | HASH JOIN

ANTI

|

|

1 |

26 |

28

(8)| 00:00:01 |

| 2

|

TABLE ACCESS FULL| LJN_TEST1 | 30000 |

380K|

13

(0)| 00:00:01 |

| 3

|

TABLE ACCESS FULL| LJN_TEST2 | 30000 |

380K|

13

(0)| 00:00:01 |

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

Predicate Information (identified by

operation id):

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

1 -

access("COL"="COL")

很好!这回oracle已经知道使用hash

join了!不过有时候表中需要存储空值,这时候就不能在表结构上指定非空了,那也同样简单:

第二种解决方案:

查询时在内表与外表中过滤空值。

先把表结构恢复为允许空值的:

alter table ljn_test1 modify col

null;

alter table ljn_test2 modify col

null;

然后改造查询:

select * from ljn_test1 where col is

not null and col not in (select col from ljn_test2 where col is not

null);

COL

----------

1

Elapsed: 00:00:00.07

Execution Plan

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

Plan hash value: 385135874

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

| Id |

Operation

| Name

| Rows |

Bytes | Cost (%CPU)| Time

|

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

| 0 |

SELECT STATEMENT

|

|

1 |

26 |

28

(8)| 00:00:01 |

|* 1 | HASH JOIN

ANTI

|

|

1 |

26 |

28

(8)| 00:00:01 |

|* 2 |

TABLE ACCESS FULL| LJN_TEST1 | 30000 |

380K|

13

(0)| 00:00:01 |

|* 3 |

TABLE ACCESS FULL| LJN_TEST2 | 30000 |

380K|

13

(0)| 00:00:01 |

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

Predicate Information (identified by

operation id):

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

1 -

access("COL"="COL")

2 -

filter("COL" IS NOT NULL)

3 -

filter("COL" IS NOT NULL)

OK! hash join出来了!我想我关于not exists与not in之间的比较也该结束了。

oracle and not 的用法,[ORACLE]详解not in与not exists的区别与用法(not in的性能并不差!)...相关推荐

  1. 详解not in与not exists的区别与用法(not in的性能并不差)

    在网上搜了下关于oracle中not exists和not in性能的比较,发现没有描述的太全面的,可能是问题太简单了,达人们都不屑于解释吧.于是自己花了点时间,试图把这个问题简单描述清楚,其实归根结 ...

  2. map函数的用法python,详解Python map函数及Python map()函数的用法

    python map函数 map()函数 map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list ...

  3. CC#中List用法介绍详解学习通http://www.bdgxy.com/roundWorker类用法总结学习通http://www.bdgxy.com/

    文章来源: 学习通http://www.bdgxy.com/ 普学网http://www.boxinghulanban.cn/ 智学网http://www.jaxp.net/ 表格制作excel教程h ...

  4. 【matlab】函数meshgrid的用法详解(生成网格矩阵)和ndgrid的区别及用法

    ------------------------------------------------------------------  meshgrid 函数用来生成网格矩阵,可以是二维网格矩阵. e ...

  5. aspx repeater 用法_详解ASP.NET数据绑定操作中Repeater控件的用法

    一.绑定控件之Repeater.NET封装了多种数据绑定控件,诸如GridView.DataList等但该篇文章将会从Repeater入手,因为Repeater只提供了基本的数据绑定模板,没有内置其它 ...

  6. java继承类型的用法_详解Java中使用externds关键字继承类的用法

    理解继承是理解面向对象程序设计的关键.在Java中,通过关键字extends继承一个已有的类,被继承的类称为父类(超类,基类),新的类称为子类(派生类).在Java中不允许多继承. (1)继承 cla ...

  7. oracle几种例外,Oracle例外用法实例详解

    本文实例讲述了Oracle例外用法.分享给大家供大家参考,具体如下: 一.例外分类 oracle将例外分为预定义例外.非预定义例外和自定义例外三种. 1).预定义例外用于处理常见的oracle错误. ...

  8. 创建emp表 oracle,Oracle中创建和管理表详解

    Oracle中创建和管理表详解 更新时间:2013年08月01日 15:44:16   作者: 以下是对Oracle中的创建和管理表进行了详细的分析介绍,需要的朋友可以过来参考下 SQL> /* ...

  9. Oracle中序列(Sequence)详解

    Oracle中序列(Sequence)详解 一 序列定义 序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).不占用磁盘空间,占用内存. 其主要用 ...

最新文章

  1. sscanf实用功能简介
  2. 圆的半径java_css中的圆形边界半径工件
  3. VS中怎样使用Nuget添加MQTTnet依赖
  4. cmd暂停快捷键_是否有键盘快捷键可以暂停正在运行的CMD窗口的输出?
  5. php的web能力,web 性能的几个概念。
  6. 计算机视觉基本原理介绍—RANSAC
  7. Atitit enhance dev effect提升开发效率的十大原理与方法v2 u66.docx Atitit enhance dev effect提升开发效率的十大原理 目录 1. 管理 2
  8. springboot基于电脑商城的购物系统l.rar(项目源码+数据库文件)
  9. 第一章:恶意软件静态分析基础
  10. wpsa4排版_WPS2000如何快速排版
  11. 领域的初学者--推荐的一本书
  12. 电机驱动 TMC5160 详解
  13. excel冻结窗口怎么设置_excel冻结多行怎么设置-和冻结首行一样哦
  14. 将心比心,你的人生道路才会更宽广
  15. swift网络请求封装(Moya)
  16. html使div内部元素水平排列_实现元素水平排列的六种方法
  17. 一大法器-----正则表达式
  18. java时间字符串转时间戳
  19. Radius认证协议(五)报文属性-3
  20. 支付宝无障碍体验提升之路

热门文章

  1. 【网络攻防技术】实验四——缓冲区溢出攻击实验
  2. 【面试】面试常问之堆栈的区别
  3. 计算机系优秀团支部申报表,2016-2017学年优秀团支部评选活动圆满结束
  4. 在标准ASCII码表中,已知英文字母K的十六进制码值是4B,则二进制ASCII码1001000对应的字符是( )
  5. mvn 默认scope_Maven依赖中的scope详解
  6. Intent传递数据的方法
  7. Linux Socket 两个客户端通信,服务端作为中转
  8. java 换行符转换
  9. h3c服务器设置管理ip配置文件,H3C 开局设置
  10. 常见的主流自动化测试框架,这5种能帮到你很多