`
楚若之夜
  • 浏览: 121341 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

MINUS,外连接,NOT IN,NOT EXISTS 的效率比较

 
阅读更多

关于 MINUS,外连接,NOT IN,NOT EXISTS 的效率比较,绝对是一个很老很老的话题了.

如果要完成这个需求:"取出一些记录,在表A中,不在表B中", 你会采用哪种方案?为什么会采用这种方案?

我作了一个实验, 发现随着数据库版本的不同,CBO的工作方式也有变化.

本文仅限于一般性的分析, 不涉及internal.

 

一.  概述
首先, 我必须纠正自己的一个"错误认识": MINUS的效率很低.
针对上面提到的需求,采用哪种查询方式,其效率取决于:

1.  两个表的数据量,以及数据分布;
2.  表有没有经过分析;
3.  子查询中是否包含NULL值 (很重要);
4.  是否存在索引;
5.  数据库版本:不同版本的数据库,优化器的工作方式会有差异.
二. 环境
首先测试的数据库的版本是Oracle 9.0.1.5,接下来我会在10G中也测试一下.

两个与优化器工作原理相关的的参数都用的是缺省值.


optimizer_index_caching
 integer 
 0
 
optimizer_index_cost_adj 
 integer 
 100
 


 

表T1,T2,结构相同,但是数据不同.T2可以看成是T1的子集.
表的结构很简单,都取自dba_objects视图
 

create table t1 as select * from dba_objects where rownum<=13000;

 

create table t2 as select * from dba_objects where rownum<=11000;

Create index ix_t2 on t2(object_id);

三. 测试
目标: 我想把T1表中其它的数据也导入到T2表.

方式: 启动SQL TRACE, 再用tkprof对生成的trace文件进行解析.

首先用 NOT IN来执行,

1. 使用 NOT IN
 select count(*) from t1 where object_id not in ( select object_id from t2);

 


call
 count
 cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.00
 0.01
 0
 0
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 16.84
 18.05
 190
 1153542
 0
 1
 
total
 4
 16.84
 18.06
 190
 1153542
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000
 TABLE ACCESS FULL T1
 
11000  
 TABLE ACCESS FULL T2
 


结论: 两次全表扫描的代价显然太高了,无疑是效率最低的方案.
2. 使用MINUS 
 alter system flush shared_pool;
 alter session set sql_trace=true;
 Select count(*) from
     (select object_id from t1
       minus
      select object_id from t2
     );

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.01
 0.00
 0
 2
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.04
 0.03
 0
 356
 0
 1
 
total
 4
 0.05
 0.03
 0
 358
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 VIEW
 
2000   
 MINUS
 
13000
 SORT UNIQUE
 
13000
 TABLE ACCESS FULL T1
 
11000 
 SORT UNIQUE
 
11000 
 TABLE ACCESS FULL T2
 


结论: 看上去效率很不错
3. 使用 not exists

 alter system flush shared_pool;
 alter session set sql_trace=true;
 select count(*)
      from t1
     where not exists
      (select null from t2 where t2.object_id = t1.object_id);

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.01
 0.00
 0
 2
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.08
 0.21
 24
 26197
 0
 1
 
total
 4
 0.09
 0.21
 24
 26199
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000
  TABLE ACCESS FULL T1
 
11000
 INDEX RANGE SCAN (object id 108538)
 


结论: 效率比NOT IN 好很多,但是不如MINUS,并且存在物理读.

4. 最后来看看我比较喜欢用的外连接(+)

 alter system flush shared_pool;
 alter session set sql_trace=true;
 select count(*)
      from t1, t2
     where t1.object_id = t2.object_id(+)
      and t2.object_id IS NULL;

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.01
 0.00
 0
 2
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.05
 0.05
 0
 13222
 0
 1
 
total
 4
 0.06
 0.05
 0
 13224
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000 
 NESTED LOOPS OUTER
 
13000
 TABLE ACCESS FULL T1
 
11000
 INDEX RANGE SCAN (object id 108538)
 


结论: 比NOT EXISTS的效果好,不如MINUS.从查询计划来看,显然不是一个最优计划.

四. 对表分析后再测试
analyze table t1 compute statistics;

1.     NOT IN

alter system flush shared_pool;
alter session set sql_trace=true;

select count(*)
from t1
where object_id not in (select object_id from t2);

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.02
 0.00
 0
 0
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 16.04
 0.05
 0
 0
 0
 1
 
total
 4
 16.06
 0.05
 0
 0
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000 
 TABLE ACCESS FULL T1
 
11000
 TABLE ACCESS FULL T2
 


结论:与分析前相比,没有任何改变
2.     MINUS

alter system flush shared_pool;
alter session set sql_trace=true;

Select count(*) from
  (select object_id from t1
     minus
   select object_id from t2
  );

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.00
 0.00
 0
 76
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.05
 0.04
 0
 356
 0
 1
 
total
 4
 0.05
 0.04
 0
 342
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 VIEW
 
2000 
 MINUS
 
13000
 SORT UNIQUE
 
13000
 TABLE ACCESS FULL T1
 
11000
 SORT UNIQUE
 
11000
 TABLE ACCESS FULL T2
 


结论: 查询计划没有改变, 虽然各项指标有些不同.

3.     使用NOT EXISTS

alter system flush shared_pool;
alter session set sql_trace=true;

select count(*)
 from t1
  where not exists
   (select null from t2 where t2.object_id = t1.object_id);

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.01
 0.02
 0
 144
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.08
 0.08
 0
 26197
 0
 1
 
total
 4
 0.09
 0.10
 0
 26341
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000 
 TABLE ACCESS FULL T1
 
11000
 INDEX RANGE SCAN (object id 108538)
 


结论: 查询计划也没有改变.

4.     使用 外连接
alter system flush shared_pool;
alter session set sql_trace=true;

select count(*)
 from t1, t2
  where t1.object_id = t2.object_id(+)
   and t2.object_id IS NULL;

alter session set sql_trace=false;

 


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.01
 0.00
 0
 1
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.02
 0.01
 0
 223
 0
 1
 
total
 4
 0.03
 0.01
 0
 224
 0
 1
 


 


Rows
 Row Source Operation
 
1
 SORT AGGREGATE
 
2000
 FILTER
 
13000 
 HASH JOIN OUTER
 
13000
 TABLE ACCESS FULL T1
 
11000
 INDEX FAST FULL SCAN (object id 108538)
 


结论: 经过分析以后,使用了HASH JOIN,效率提高很明显.这是一个正确的查询计划.

 

总结:这四种查询模式中使用外连接的效率最高.

五. 在10G中测试
T1: 10000
T2: 9800

NOT IN


call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.00
 0.00
 0
 0
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 7.65
 7.47
 135
 685810
 0
 1
 
total
 4
 7.65
 7.47
 135
 685810
 0
 1
 


执行计划:


Rows
 Row Source Operation
 
1
 SORT AGGREGATE (cr=685810 pr=135 pw=0 time=7479614 us)
 
200
 FILTER  (cr=685810 pr=135 pw=0 time=7474258 us)
 
10000 
 TABLE ACCESS FULL T1 (cr=138 pr=135 pw=0 time=40407 us)
 
9800
 TABLE ACCESS FULL T2 (cr=685672 pr=0 pw=0 time=7366891 us)
 


对T1进行分析后

call
 count
 Cpu
 elapsed
 disk
 query
 current
 rows
 
Parse
 1
 0.00
 0.01
 0
 3
 0
 0
 
Execute
 1
 0.00
 0.00
 0
 0
 0
 0
 
Fetch
 2
 0.01
 0.01
 22
 165
 0
 1
 
total
 4
 0.01
 0.02
 22
 168
 0
 1
 


执行计划:

Rows
 Row Source Operation
 
1
 SORT AGGREGATE (cr=165 pr=22 pw=0 time=15933 us)
 
200
 HASH JOIN ANTI (cr=165 pr=22 pw=0 time=15973 us)
 
10000 
 TABLE ACCESS FULL T1 (cr=138 pr=0 pw=0 time=10075 us)
 
9800
 INDEX FAST FULL SCAN IX_T2 (cr=27 pr=22 pw=0 time=10529 us)(object id 52081)
 


另外, 通过对NOT EXISTS的分析,发现它的效率要好于MINUS,这也是一个变化.

 

六. 结束语
从上面的对比分析,可以得出这样的结论:

10G的CBO要比9i的CBO智能了不少, 对于NOT IN 和NOT EXISTS 这两种使用频率较高的语句,能使用最优的查询计划.

分享到:
评论

相关推荐

    SQL语句教程.doc

    IN 5 BETWEEN 6 LIKE 7 ORDER BY 8 函数 10 COUNT 11 GROUP BY 12 HAVING 13 ALIAS 13 表格链接 15 外部链接 16 CONCATENATE 17 SUBSTRING 18 TRIM 19 表格处理 21 CREATE TABLE 21 CONSTRAINT 22 NOT NULL 22 ...

    java替换html特殊字符工具类

    htmlStr =htmlStr.replaceAll("&notin;", "∉"); htmlStr =htmlStr.replaceAll("&ni;", "∋"); htmlStr =htmlStr.replaceAll("&prod;", "∏"); htmlStr =htmlStr.replaceAll("&sum;", "∑"); htmlStr =...

    HTML 符号实体参考手册

    ...本字符实体参考手册包括了数学符号、希腊字符、各种箭头记号、科技符号以及形状。...notin ∋ ∋ ∋ ni ∏ ∏ ∏ prod ∑ ∑ ∑ sum − − − minus ∗ ∗ ∗ lowast √ √ √ square root

    SQL语句教程.pdf

    IN BETWEEN LIKE ORDER BY 函数 COUNT GROUP BY HAVING ALIAS 表格链接 外部链接 CONCATENATE SUBSTRING TRIM 表格处理 CREATE TABLE CONSTRAINT NOT NULL UNIQUE CHECK 主键 外来键 CREATE VIEW CREATE INDEX ALTER...

    Oracle_Database_11g完全参考手册.part3/3

    13.2.3 用外部连接代替NOTIN 13.2.4 用NOTEXISTS代替NOTIN 13.3 自然连接和内部连接 13.4 UNION、INTERSECT和MINUS 13.4.1 IN子查询 13.4.2 UNION、INTERSECT和MiNUS的限制 第14章 一些复杂的技术 14.1 复杂的分组 ...

    Oracle8i_9i数据库基础

    §10.1.8 Not in和Not Exists 218 §10.1.9 关于 COPY命令 218 §10.1.10 列值为NULL情形的处理 219 §10.1.11 使用 product_user_file来限制用户使用产品 220 §10.2 常用技巧 221 §10.2.1 long 类型的查询 222 §...

    Oracle_Database_11g完全参考手册.part2/3

    13.2.3 用外部连接代替NOTIN 13.2.4 用NOTEXISTS代替NOTIN 13.3 自然连接和内部连接 13.4 UNION、INTERSECT和MINUS 13.4.1 IN子查询 13.4.2 UNION、INTERSECT和MiNUS的限制 第14章 一些复杂的技术 14.1 复杂的分组 ...

    2009达内SQL学习笔记

    可以使用AND或OR、NOT等连接相关的条件 计算次序可以通过圆括号()来明确地分组。不要过分依赖默认计算次序,使用圆括号()没有坏处,它能消除二义性。 and:条件与 如 SELECT prod_id,prod_price,prod_name FROM...

    数据库基础

    §10.1.8 Not in和Not Exists 218 §10.1.9 关于 COPY命令 218 §10.1.10 列值为NULL情形的处理 219 §10.1.11 使用 product_user_file来限制用户使用产品 220 §10.2 常用技巧 221 §10.2.1 long 类型的查询 222 §...

    VB编程资源大全(英文源码 控制)

    listtxt.zip This shows how to retrieve the text from the current selection in a listbox.&lt;END&gt;&lt;br&gt;11 , list_index.zip This example tells you if the inputted List Index is selected or not.&lt;END&gt;...

    GENESIS 2000 DFM PE

    not added on as scripts to be interpreted slowly but as fully compiled and integrated functions of the system. Once installed, the customized features will be retained with each upgrade of the system ...

Global site tag (gtag.js) - Google Analytics