轉自http://www.2cto.com/database/201310/251176.html
對於in和exists、not in和not exists還是有很多的人有疑惑,更有甚者禁用not in,所有的地方都要用not exists,它真的高效嗎?
【實驗1 in和exists原理及性能比較】
准備數據
create table test1 as select * from dba_objects where rownum <=1000;
create table test2 as select * from dba_objects;
exec dbms_stats.gather_table_stats(user,'test1');
exec dbms_stats.gather_table_stats(user,'test2');
set autotrace traceonly
in 查詢
select * from test1 t1 where t1.object_id in (select t2.object_id from test2 t2);
執行計划
----------------------------------------------------------
Plan hash value: 3819917785
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 90000 | 307 (1) | 00:00:04 |
|* 1 | HASH JOIN SEMI | | 1000 | 90000 | 307 (1) | 00:00:04 |
| 2 | TABLE ACCESS FULL | TEST1 | 1000 | 85000 | 6 (0) | 00:00:01 |
| 3 | TABLE ACCESS FULL | TEST2 | 73119 | 357K| 301 (1) | 00:00:04 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
統計信息
----------------------------------------------------------
1 recursive calls
0 db block gets
98 consistent gets
0 physical reads
0 redo size
50936 bytes sent via SQL*Net to client
1226 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed
exists 查詢
select * from test1 t1 where exists(select 1 from test2 t2 where t1.object_id=t2.object_id);
執行計划
----------------------------------------------------------
Plan hash value: 3819917785
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 90000 | 307 (1) | 00:00:04 |
|* 1 | HASH JOIN SEMI | | 1000 | 90000 | 307 (1) | 00:00:04 |
| 2 | TABLE ACCESS FULL | TEST1 | 1000 | 85000 | 6 (0) | 00:00:01 |
| 3 | TABLE ACCESS FULL | TEST2 | 73119 | 357K| 301 (1) | 00:00:04 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
統計信息
----------------------------------------------------------
1 recursive calls
0 db block gets
98 consistent gets
0 physical reads
0 redo size
50936 bytes sent via SQL*Net to client
1226 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed
結論:
在oracle 11g中,in和exists 其實是一樣的,原理就是兩張表做HASH JOIN SEMI。也可以通過10053事件看到兩條sql語句最終轉換成同一條sql。
【實驗2 not in和not exists原理及性能比較】
not exists 比 not in 效率高的例子(按照轉載文章實驗,執行計划和文章不符,結果是效率相同,可能是由於本人使用版本11g高於原文章緣故)
保持test1 和 test2 數據不變,分別是 1000、70000+
select count(*) from test1 where object_id not in (select object_id from test2);
select count(*) from test1 t1 where not exists(select 1 from test2 t2 where t1.object_id=t2.object_id);
執行計划相同,此處就省略了。
執行計划相同;效率一樣
not in 比 not exists 效率高的例子(依然和轉載文章結果不符,結果還是效率相同,后來我用hint改變了not in的執行計划才能顯示出not in的優勢)
准備數據
創建表t1和t2,結構和test1、test2一樣,但是t1數據量為5條,t2數據量為20W+
select count(*) from t1 where object_id not in (select /*+ no_unnest */ object_id from t2);
--注意:如果不用hint來改變執行計划,兩個語句仍然是一樣的執行計划;
執行計划
----------------------------------------------------------
Plan hash value: 59119136
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 755 (1)| 00:00:10 |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
|* 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL| T1 | 5 | 15 | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| T2 | 2 | 10 | 301 (1)| 00:00:04 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( NOT EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "T2" "T2"
WHERE LNNVL("OBJECT_ID"<>:B1)))
4 - filter(LNNVL("OBJECT_ID"<>:B1))
統計信息
----------------------------------------------------------
1 recursive calls
0 db block gets
23 consistent gets
0 physical reads
0 redo size
522 bytes sent via SQL*Net to client
500 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
select count(*) from t1 where not exists (select 1 from t2 where t1.object_id=t2.object_id);
執行計划
----------------------------------------------------------
Plan hash value: 1513027705
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 2376 (1)| 00:00:29 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
|* 2 | HASH JOIN ANTI | | 1 | 8 | 2376 (1)| 00:00:29 |
| 3 | TABLE ACCESS FULL| T1 | 5 | 15 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| T2 | 584K| 2856K| 2371 (1)| 00:00:29 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
統計信息
----------------------------------------------------------
1 recursive calls
0 db block gets
8599 consistent gets
0 physical reads
0 redo size
522 bytes sent via SQL*Net to client
500 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
結論
在11g版本中,數據量如我制造類似情況下,in和exists,not in和not exists的執行計划已經基本一致了,更傾向於使用HASH JOIN,但是當外表非常小,內表非常大的情況下,通過hint改變執行計划,filter的性能可以更優於HASH JOIN,也說明了not in不一定性能比not exists 差。