`
qsky
  • 浏览: 46572 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

对IN & EXISTS NOT IN & NOT EXISTS的优化

    博客分类:
  • DB
阅读更多
转自http://yaanzy.itpub.net/post/1263/10217
在平时工作中,IN & EXISTS NOT IN & NOT EXISTS是使用频率比较高的SQL语句,
所以对它们的优化工作是很有必要的

测试环境:Oracle 9.2.0.1 for Windows2000



1、IN 和 EXISTS

IN和EXISTS的处理流程是不一样的:

IN的执行流程
select * from T1 where x in ( select y from T2 )
可以理解为:
select * from t1, ( select distinct y from t2 ) t2 where t1.x = t2.y;

EXISTS的执行流程
select * from t1 where exists ( select null from t2 where y = x )
可以理解为:
for x in ( select * from t1 )
loop
if ( exists ( select null from t2 where y = x.x )
then
OUTPUT THE RECORD
end if
end loop

创建测试用例表big(4000 row)和small(400 row)

create table big as select * from dba_objects where rownum <= 10000;
insert into big select * from big;
insert into big select * from big;
commit;
create table small as select * from dba_objects where rownum <= 400;

当内层表为small,外层表为big时,两种语法的查询如下:

SQL> select count(1) from big a where a.object_id in
(select b.object_id from sall b);

COUNT(1)
----------
1600

已用时间: 00: 00: 00.56

Execution Plan
-----------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 MERGE JOIN
3 2 SORT (JOIN)
4 3 TABLE ACCESS (FULL) OF 'BIG'
5 2 SORT (JOIN)
6 5 VIEW OF 'VW_NSO_1'
7 6 SORT (UNIQUE)
8 7 TABLE ACCESS (FULL) OF 'SMALL'

Statistics
-----------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads

SQL> select count(1) from big a where exists
(select 1 from small b where a.object_id=b.object_id);

COUNT(1)
----------
1600

已用时间: 00: 00: 03.10

Execution Plan
-----------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF 'BIG'
4 2 TABLE ACCESS (FULL) OF 'SMALL'

Statistics
-----------------------------------------------------
0 recursive calls
0 db block gets
312157 consistent gets
0 physical reads

当内层表为big,外层表为small时,两种语法的查询如下:

SQL> select count(1) from small a where a.object_id in
(select b.object_id from big b);

COUNT(1)
----------
400

已用时间: 00: 00: 00.56

Execution Plan
-----------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 MERGE JOIN
3 2 SORT (JOIN)
4 3 TABLE ACCESS (FULL) OF 'SMALL'
5 2 SORT (JOIN)
6 5 VIEW OF 'VW_NSO_1'
7 6 SORT (UNIQUE)
8 7 TABLE ACCESS (FULL) OF 'BIG'

Statistics
-----------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads

SQL> select count(1) from small a where exists
(select null from big b where a.bject_id=b.object_id);

COUNT(1)
----------
400

已用时间: 00: 00: 00.25

Execution Plan
-----------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF 'SMALL'
4 2 TABLE ACCESS (FULL) OF 'BIG'

Statistics
-----------------------------------------------------
0 recursive calls
0 db block gets
2562 consistent gets
0 physical reads


在对表big、small进行分析后,发现CBO下两种语法的执行计划是一样的,都使用hash连接或者hash半连接

SQL> analyze table big compute statistics;
SQL> analyze table small compute statistics;

SQL> select count(1) from big a where a.object_id in
(select b.object_id from small b);

COUNT(1)
----------
1600

已用时间: 00: 00: 00.09

Execution Plan
-------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=58
Card=1 Bytes=8)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=58 Card=1600 Bytes=12800)
3 2 SORT (UNIQUE)
4 3 TABLE ACCESS (FULL) OF 'SMALL' (Cost=2 Card=400 Bytes=1600)
5 2 TABLE ACCESS (FULL) OF 'BIG' (Cost=53 Card=40000 Bytes=160000)

Statistics
-------------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads

SQL> select count(1) from big a where exists
(select 1 from small b where a.object_id=b.object_id);

COUNT(1)
----------
1600

已用时间: 00: 00: 00.09

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=58 Card=1 Bytes=8)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=58 Card=1600 Bytes=12800)
3 2 SORT (UNIQUE)
4 3 TABLE ACCESS (FULL) OF 'SMALL' (Cost=2 Card=400 Bytes=1600)
5 2 TABLE ACCESS (FULL) OF 'BIG' (Cost=53 Card=40000 Bytes=160000)

Statistics
--------------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads


SQL> select count(1) from small a where a.object_id in
(select b.object_id from big b);

COUNT(1)
----------
400

已用时间: 00: 00: 00.09

Execution Plan
------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=56 Card=1 Bytes=8)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (SEMI) (Cost=56 Card=400 Bytes=3200)
3 2 TABLE ACCESS (FULL) OF 'SMALL' (Cost=2 Card=400 Bytes=1600)
4 2 TABLE ACCESS (FULL) OF 'BIG' (Cost=53 Card=40000 Bytes=160000)

Statistics
------------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads

SQL> select count(1) from small a where exists
(select 1 from big b where a.object_id=b.object_id);

COUNT(1)
----------
400

已用时间: 00: 00: 00.09

Execution Plan
-------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=56 Card=1 Bytes=8)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (SEMI) (Cost=56 Card=400 Bytes=3200)
3 2 TABLE ACCESS (FULL) OF 'SMALL' (Cost=2 Card=400 Bytes=1600)
4 2 TABLE ACCESS (FULL) OF 'BIG' (Cost=53 Card=40000 Bytes=160000)

Statistics
-------------------------------------------------------
0 recursive calls
0 db block gets
543 consistent gets
0 physical reads

删除表分析,使用提示/*+ use_hash(a,b) */ 或者 /*+ hash_sj */,
两种语法都可以达到CBO的执行计划

SQL> analyze table big delete statistics;
SQL> analyze table small delete statistics;

SQL> select /*+ use_hash(a,b) */count(1) from big a where a.object_id in
(select b.object_id from small b);

SQL> select /*+ use_hash(a,b) */count(1) from big a where exists
(select 1 from small b where a.object_id=b.object_id);

SQL> select count(1) from small a where a.object_id in
(select /*+ hash_sj */ b.object_id from big b);

SQL> select count(1) from small a where exists
(select /*+ hash_sj */ 1 from big b where a.object_id=b.object_id);

下表列出了各种情况下的速度情况:
┌───────────┬──────────────────────────┬─────────────────────────┬─────────────┐
│ │ outer big,inner small │ outer small,inner big │ table rows │
├───────────┼──────────┬───────────────┼──────────┬──────────────┼─────────────┤
│ │ IN SQL │ EXISTS SQL │ IN SQL │ EXISTS SQL │ │
├───────────┼──────────┼───────────────┼──────────┼──────────────┼─────────────┤
│un-analyze │ 0.56s │ 3.10s │ 0.56s │ 0.25s │ big=40000 │
├───────────┼──────────┼───────────────┼──────────┼──────────────┤ and │
│ analyzed │ 0.09s │ 0.09s │ 0.09s │ 0.09s │ small=400 │
├───────────┼──────────┼───────────────┼──────────┼──────────────┼─────────────┤
│un-analyze │ 0.72s │ 3.53s │ 0.25s │ 2.97s │ big=5000 │
├───────────┼──────────┼───────────────┼──────────┼──────────────┤ and │
│ analyzed │ 0.09s │ 0.09s │ 0.09s │ 0.09s │ small=4000 │
└───────────┴──────────┴───────────────┴──────────┴──────────────┴─────────────┘

结论:在未对表进行分析前,若两个表数据量差异很大,则外层表是大表时使用IN较快,
外层表是小表时使用EXISTS较快;若两表数据量接近,则使用IN较快;
分析表后无论用IN还是EXISTS都变得更快,由于执行计划一样,所以速度一样;

所以:无论使用IN还是EXISTS,只要使用散列连接,即提示/*+ use_hash(a,b) */,
或者在子句中散列半连接提示/*+ hash_sj */, 就使其达到最优速度;

附注:半连接的提示有hash_sj、merge_sj、nl_sj


***********************************************************************************************************************
***********************************************************************************************************************


2、NOT IN 和 NOT EXISTS

NOT EXISTS的执行流程
select .....
from rollup R
where not exists ( select 'Found' from title T
where R.source_id = T.Title_ID);
可以理解为:
for x in ( select * from rollup )
loop
if ( not exists ( that query ) ) then
OUTPUT
end if;
end;

注意:NOT EXISTS 与 NOT IN 不能完全互相替换,看具体的需求。如果选择的列可以为空,则不能被替换。

对于not in 和 not exists的性能区别:
not in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,
子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.

如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用/*+ HASH_AJ */
或者外连接+is null,NOT IN 在基于成本的应用中较好

创建测试用例表big(40000 row)和small(1000 row):

truncate table big;
truncate table small;
insert into big select * from dba_objects where rownum <=20000;
insert into big select * from dba_objects where rownum <=20000;
insert into small select * from dba_objects where rownum <=1000;
commit;

基本句型:
<1> not in
SQL> select count(1) from big a where a.object_id not in (select b.object_id from small b);

COUNT(1)
----------
38000

已用时间: 00: 00: 12.56

Execution Plan
-----------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF 'BIG'
4 2 TABLE ACCESS (FULL) OF 'SMALL'


Statistics


分享到:
评论
2 楼 aop 2009-03-03  
zl584521 写道

我对于程序已经失去了兴趣.....


为啥捏?
1 楼 zl584521 2008-03-06  
我对于程序已经失去了兴趣.....

相关推荐

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句.txt

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句.txt欢迎下载!

    sql in,exists,not in,not exists区别

    SQL 中 IN、EXISTS、NOT IN、NOT EXISTS 的区别 IN、EXISTS、NOT IN、NOT EXISTS 是 SQL 中四种常用的条件判断运算符,它们之间的区别主要体现在使用场景、执行效率和语法结构上。 IN IN 是一种条件判断运算符,...

    sql case when exists not exists in not in

    在SQL查询中,`CASE WHEN`、`EXISTS`、`NOT EXISTS`以及`IN`和`NOT IN`是常用的操作符,它们用于处理复杂的条件判断和数据筛选。这些概念对于理解和编写高效的SQL语句至关重要,尤其是在数据分析和数据库管理中。 `...

    sql语句优化之用EXISTS替代IN、用NOT EXISTS替代NOT IN的语句

    SQL语句优化之用EXISTS替代IN、用NOT EXISTS替代NOT IN的语句 SQL语句优化是数据库性能优化的重要方面之一。在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下,使用EXISTS...

    IN&amp;EXISTS与NOT IN&amp;NOT EXISTS 的优化原则小结

    - 在某些情况下,可以考虑使用外连接或`IS NULL`结合`NOT IN`来替代`NOT EXISTS`,尤其是在基于成本的优化器中,这可能带来更好的性能。 6. 总结: `IN`和`EXISTS`主要取决于哪个查询能更快地返回结果,而`NOT IN...

    经典SQL查询总结关于Exists,not Exists.in ,not in效率的说明。

    ### 经典SQL查询总结关于Exists, not Exists, IN, not IN 效率的说明 在数据库查询操作中,存在着多种方法来实现相似的功能,但不同的实现方式在性能上可能会有显著差异。本文将深入探讨 SQL 中 `EXISTS`, `NOT ...

    EXISTS_和_NOT_EXISTS

    - 效率:在某些情况下,`EXISTS` 和 `NOT EXISTS` 可能会提供相似的性能,但通常 `IN` 和 `NOT IN` 比较操作符在处理大量数据时更有效。然而,当涉及到子查询时,`EXISTS` 和 `NOT EXISTS` 可能更合适,因为它们只...

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句2.txt

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句2.txt,欢迎下载!

    INEXISTS与NOT INNOT EXISTS 的优化原则小结

    在SQL查询优化中,`IN`、`EXISTS`、`NOT IN`和`NOT EXISTS`是四个常见的比较和过滤条件,它们在不同场景下有不同的性能表现。以下是对这些操作符优化原则的详细说明: 1. **EXISTS的执行流程**: `EXISTS` 子查询...

    exists 和 not exists的详细解释

    exists 和 not exists的详细解释

    MySQL中的in,exists,not in,not exists查询过程对比及结论

    MySQL优化之in,exists,not in,not exists的区别in与existsin查询过程结论:exists查询过程:结论:not in与not existsnot in查询过程:结论:not exists查询过程:结论: 首先我们使用两个用户表作为实例 insert ...

    sql exists和not exists用法

    相较于`IN`、`NOT IN`等操作,`EXISTS`与`NOT EXISTS`具有更高的效率,尤其是在处理大型数据集时。 #### EXISTS 介绍 `EXISTS`关键字用于检查子查询是否至少返回一行数据。如果子查询返回至少一行数据,则`EXISTS`...

    Oracle In和exists not in和not exists的比较分析

    在Oracle数据库中,`IN`、`EXISTS`、`NOT IN` 和 `NOT EXISTS` 是四个常用的子查询操作符,它们在SQL查询语句中扮演着不同的角色,且各有其性能特点。以下是对这些操作符的详细分析和比较。 1. `IN` 操作符: `IN` ...

    NOT EXISTS ⇔ NOT IN ⇒ NOT NULL 问题 结果不一样 疑问 如果等效

    在SQL查询语言中,`NOT EXISTS`、`NOT IN` 和 `NOT NULL` 是三种用于排除特定条件的数据记录的方法。然而,在实际应用中,这三种语法有着不同的应用场景和执行逻辑,有时初学者可能会误以为它们是等效的,但实际上...

    一次SQL Tuning引出来的not in , not exists 语句的N种写法2

    【描述】:文章并未直接提供描述,但从标题推测,该内容可能涉及数据库查询优化,特别是关于`not in`和`not exists`两种SQL查询子句的不同使用方式和性能比较。 【标签】:SQL优化,查询性能,数据库管理 【正文】...

    exists SQL用法详解、exists和not exists的常用示例

    在SQL查询中,`EXISTS`和`NOT EXISTS`是两个非常重要的子查询操作符,它们主要用于判断子查询是否返回结果。本篇文章将详细介绍这两个关键字的用法,并通过实例进行解析。 首先,`EXISTS`的语法是:主查询中的条件...

    in exist not_in

    IN、EXISTS、NOT EXISTS、NOT IN 在 SQL 语句中的应用和区别 IN 语句和 EXISTS 语句都是 SQL 语句中用来判断是否存在某个值的语句,但是它们的实现机制和应用场景是不同的。 IN 语句是通过 hash 连接来实现的,它...

    oracle中exists_和in的效率问题详解

    一般来说,EXISTS 适合外表结果集小的情况,因为 EXISTS 是对外表作 loop,每次 loop 再对那表进行查询。IN 适合内外表都很大的情况,因为 IN 是把外表和那表作 hash join。 在某些情况下,EXISTS 可以比 IN 快很多...

Global site tag (gtag.js) - Google Analytics