关于 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 这两种使用频率较高的语句,能使用最优的查询计划.
相关推荐
在性能方面,`EXISTS`通常比`IN`和`MINUS`更快,因为它在找到一个匹配项后就会停止执行,而`IN`和`MINUS`会完成整个集合的比较。然而,这取决于具体的数据分布和索引策略,因此在实际应用中,应根据具体情况选择最...
在MySQL数据库中,`NOT IN` 和 `MINUS` 是两种常用的子查询操作,用于从结果集中排除特定的行。然而,这些操作在某些情况下可能会导致性能问题,特别是在处理大量数据时。本篇文章将探讨如何对这两种操作进行优化,...
6. **MINUS子句和NOT IN**:MINUS子句通常比NOT IN更快,但需注意,NOT IN和NOT EXISTS在处理NULL值时有所不同,NOT EXISTS在某些情况下可能更快,因为它可以使用索引。 7. **LIKE操作符的效率**:`LIKE`操作符,...
高级查询还包括了对多个表的连接操作,如简单连接、内连接、外连接和交叉连接: - **简单连接**(JOIN):连接两个或更多表,没有特定连接条件。 - **内连接**(INNER JOIN):只返回两个表中匹配的行。 - **外连接...
1. 尽量少用嵌套查询,使用 not exist 代替 not in 子句。 2. 用多表连接代替 EXISTS 子句。 3. 少用 DISTINCT,用 EXISTS 代替。 4. 使用 UNION ALL、MINUS、INTERSECT 提高性能。 5. 使用 ROWID 提高检索速度。 6....
SELECT id, name FROM emp1 e WHERE e.id NOT IN (SELECT id FROM sext); ``` 2. 使用MINUS操作符: ```sql SELECT id FROM emp1 MINUS SELECT id FROM sext; ``` 3. 使用<> ALL子查询: ```sql SELECT * ...
- 方法a:`SELECT * FROM A WHERE A.C NOT IN (SELECT C FROM B)`可能效率较低。 - 方法b:`SELECT A.* FROM A, B WHERE A.C = B.C (+) AND B.C IS NULL`使用外连接,但注意性能问题。 - 方法c:`SELECT * FROM A...
此题使用自连接和集合操作(如`MINUS`或`EXCEPT`,在某些数据库系统中可能为`NOT EXISTS`)找出这些学生。 以上就是基于题目内容的SQL知识点解析,涵盖了子查询、JOIN操作、聚合函数、条件过滤、集合操作等核心概念...
1. 子查询和集合操作:`IN`, `NOT IN`, `MINUS`, `ALL`, `EXISTS`, `INTERSECT`。 2. `JOIN`操作:`LEFT JOIN`, `INNER JOIN`,以及如何使用连接条件。 3. 聚合函数:`COUNT`, `SUM`,以及`GROUP BY`语句。 4. 计算...
左外连接会返回所有员工记录,如果性别为空,则`s.sex`为`NULL`。 5. 使用`NOT EXISTS`: ```sql SELECT * FROM emp1 e WHERE NOT EXISTS (SELECT * FROM sext s WHERE e.id = s.id); ``` 这里查找的是不存在...
1. **减少嵌套查询**:用`NOT EXISTS`替代`NOT IN`,减少表扫描。 2. **多表连接代替EXISTS**:在适当情况下,用多表连接替换`EXISTS`子句。 3. **避免DISTINCT**:使用`EXISTS`代替`DISTINCT`,提高效率。 4. **...
此方法通过自连接实现,通过比较两支球队的名称来确保只输出一次比赛组合,避免了重复。 ### 4. 查询所有月份的发生额都比特定科目高的科目 题目要求查询所有月份的发生额都比指定科目(例如101科目)的发生额高的...
如果希望避免使用MINUS,可以使用NOT EXISTS子句或者LEFT JOIN来达到类似的效果。 接下来,我们将讨论高级SQL JOIN操作符语法。JOIN是连接两个或更多表的关键字,以基于它们之间的关联获取数据。例如,NATURAL JOIN...
1. 尽量少用嵌套查询,如必须,请用 not exist 代替 not in 子句。 2. 用多表连接代替 EXISTS 子句。 3. 少用 DISTINCT,用 EXISTS 代替。 4. 使用 UNION ALL、MINUS、INTERSECT 提高性能。 5. 使用 ROWID 提高检索...
注意,GROUP BY不能与WHERE中的聚合函数一起使用,通常可以使用EXISTS替换IN,但在包含聚合函数的IN子句中,不能用EXISTS替换。 3. 球队比赛组合: 给定一个包含球队名称的表,查询所有可能的比赛组合。这个问题...
6. **子查询优化**:合理使用`IN`、`NOT IN`、`EXISTS`和`NOT EXISTS`。当子查询结果无重复时,`IN`和`EXISTS`可以互换;当保证子查询结果非空时,`NOT IN`和`NOT EXISTS`可用`LEFT JOIN`替代。尽量避免不必要的子...
- Oracle 支持 `INTERSECT` 和 `MINUS` 集合运算符,而 SQL Server 用 `EXISTS` 和 `NOT EXISTS` 子句来实现相同的效果。 对于示例中的 `INTERSECT` 和 `MINUS` 运算符,可以使用 `EXISTS` 或 `NOT EXISTS` 来达到...
NOT EXISTS也比NOT IN更高效,原因与EXISTS类似。 #### 18. EXISTS与DISTINCT 在某些情况下,使用EXISTS代替DISTINCT可以提高性能。 #### 19. SQL语句的编写技巧 编写高效的SQL语句需要注意多方面的细节,如避免...
Oracle 的 From 子句表的顺序,Where 子句表达式的顺序,避免使用 ‘*’,使用 Truncate 代替 Delete,使用索引尽量减少对表的查询,避免使用右匹配或者中间匹配的模糊查询,并注意使用 exists、NOT EXISTS、NOT IN ...