循环嵌套链接(NESTED LOOP)
|
- Oracle 优化器根据基于规则RBO或基于成本CBO的原则,选择两个表中的一个作为驱动表,并指定其为外部表。
- Oracle 优化器再将另外一个表指定为内部表。
- Oracle从外部表中读取第一行,然后和内部表中的数据逐一进行对比,所有匹配的记录放在结果集中。
- Oracle读取外部表中的第二行,再和内部表中的数据逐一进行对比,所有匹配的记录添加到结果集中。
- 重复上述步骤,直到外部表中的所有记录全部处理完。
- 最后产生满足要求的结果集。
SQL> create index idx_t2 on t2(col2);
Index created. |
SQL> select * from v$version;
BANNER ———————————————————————————————————————— Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 – 64bit Production PL/SQL Release 11.2.0.4.0 – Production CORE 11.2.0.4.0 Production TNS for Linux: Version 11.2.0.4.0 – Production NLSRTL Version 11.2.0.4.0 – Production |
SQL> select /*+ ordered use_nl(t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan ———————————————————- Plan hash value: 1054738919 ————————————————————————————— | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ————————————————————————————— | 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 | | 1 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 | | 2 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | IDX_T2 | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 5 | 1 (0)| 00:00:01 | ————————————————————————————— Predicate Information (identified by operation id): ————————————————— 4 – access("T1"."COL2"="T2"."COL2") |
SQL> select /*+ use_nl(t1 t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan ———————————————————- Plan hash value: 1054738919 ————————————————————————————— | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ————————————————————————————— | 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 | | 1 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 | | 2 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | IDX_T2 | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 5 | 1 (0)| 00:00:01 | ————————————————————————————— Predicate Information (identified by operation id): ————————————————— 4 – access("T1"."COL2"="T2"."COL2") |
群集链接( CLUSTER JOIN )
|
排序合并链接( SORT MERGE JOIN )
|
- 优化器判断第一个源表是否已经排序,如果已经排序,则到第3步,否则到第2步。
- 第一个源表排序。
- 优化器判断第二个源表是否已经排序,如果已经排序,则到第5步,否则到第4步。
- 第二个源表排序。
- 已经排过序的两个源表进行合并操作,并生成最终的结果集。
SQL> select /*+ ordered use_merge(t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan ———————————————————- Plan hash value: 412793182 —————————————————————————- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | —————————————————————————- | 0 | SELECT STATEMENT | | 3 | 60 | 8 (25)| 00:00:01 | | 1 | MERGE JOIN | | 3 | 60 | 8 (25)| 00:00:01 | | 2 | SORT JOIN | | 3 | 45 | 4 (25)| 00:00:01 | | 3 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 | |* 4 | SORT JOIN | | 3 | 15 | 4 (25)| 00:00:01 | | 5 | TABLE ACCESS FULL| T2 | 3 | 15 | 3 (0)| 00:00:01 | —————————————————————————- Predicate Information (identified by operation id): ————————————————— 4 – access("T1"."COL2"="T2"."COL2") filter("T1"."COL2"="T2"."COL2") Note —– – dynamic sampling used for this statement (level=2) |
笛卡尔链接 ( CARTESIAN JOIN )
|
SQL> select t1.col1,t1.col2,t2.col3 from t1,t2;
9 rows selected. Execution Plan ———————————————————- Plan hash value: 787647388 —————————————————————————– | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | —————————————————————————– | 0 | SELECT STATEMENT | | 9 | 162 | 9 (0)| 00:00:01 | | 1 | MERGE JOIN CARTESIAN| | 9 | 162 | 9 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 | | 3 | BUFFER SORT | | 3 | 9 | 6 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL | T2 | 3 | 9 | 2 (0)| 00:00:01 | —————————————————————————– Note —– – dynamic sampling used for this statement (level=2) |
哈希链接( HASH JOIN )
|
- 第二个大表进行扫描。
- 如果大表不能完全cache到可用内存的时候,大表同样会分成很多分区。
- 大表的第一个分区cache到内存。
- 对大表第一个分区的数据进行扫描,并与哈希表进行比较,如果有匹配的记录,添加到结果集里面。
- 与第一个分区一样,其他的分区也类似处理。
- 所有的分区处理完后,Oracle对产生的结果集进行归并,汇总,产生最终的结果。
SQL> select /*+ use_hash(t1 t2)*/t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan ———————————————————- Plan hash value: 1838229974 ————————————————————————— | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ————————————————————————— | 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 | |* 1 | HASH JOIN | | 3 | 60 | 6 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| T2 | 3 | 15 | 3 (0)| 00:00:01 | ————————————————————————— Predicate Information (identified by operation id): ————————————————— 1 – access("T1"."COL2"="T2"."COL2") |
反连接( Anti Join )
|
select * from t1 where col2 not in (select col2 from t2);
select * from t1 where col2 <>all(select col2 from t2); select * from t1 where not exists (select 1 from t2 where col2=t1.col2); SQL> select * from t1 where col2 not in (select col2 from t2); SQL> select * from t1 where col2 <>all(select col2 from t2); SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2); |
SQL> insert into t1 values (4,null);
1 row created. SQL> commit; SQL> select * from t1; –重新执行刚才的三个SQL SQL> select * from t1 where col2 <>all(select col2 from t2); SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2); |
SQL> delete from t1 where col1=4;
1 row deleted. SQL> insert into t2 values (null,‘E2’); SQL> commit; SQL> select * from t2; –重新执行上面的三个SQL SQL> select * from t1 where col2 <>all(select col2 from t2); SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2); |
(1) 当表的连接列出现了NULL值,上述范例中的SQL就不等价了。
(2) NOT IN 和 <>ALL 对NULL值敏感,这意味着NOT IN后面的子查询或常量集合一旦有NULL值出现,则整个SQL的执行结果就会为NULL,不会包含任何记录 (3) NOT EXISTS对NULL值不敏感,也就是说NULL值对NOT EXISTS的执行结果不会有什么影响。 |
如果把该隐含参数改为FALSE,则Oracle不能再用改良反连接,而又因为NOT IN对NULL值敏感,所以Oracle此时也不能用普通的反连接。
SQL> alter session set "_OPTIMIZER_NULL_AWARE_ANTIJOIN" = false;
Session altered. SQL> select * from t1 where col2 <>all(select col2 from t2); 可以看到这里的执行计划走了FILTER,而不再是HASH JOIN ANTI NA。 |
半连接( Semi Join )
|
select * from t1 where col2 in (select col2 from t2);
select * from t1 where col2=any (select col2 from t2); select * from t1 where exists (select 1 from t2 where col2=t1.col2); SQL> select * from t1 where col2 in (select col2 from t2); SQL> select * from t1 where col2=any (select col2 from t2); SQL> select * from t1 where exists (select 1 from t2 where col2=t1.col2); 上述三个SQL的执行结果是一样的,而且它们的执行计划的现实内容均有关键字SEMI,这个关键字就说明Oracle执行这三个SQL是在用半连接。 |
几种主要表连接的比较
|
类别 |
嵌套循环链接 |
排序合并链接 |
哈希链接 |
优化器提示 |
USE_NL |
USE_MERGE |
USE_HASH |
使用的条件 |
任何链接 |
主要用于不等价链接,如<、 <=、 >、 >=;但是不包括<> |
仅用于等价链接 |
相关资源 |
CPU、磁盘I/O |
内存、临时空间 |
内存、临时空间 |
特点 |
当有高选择性索引或进行限制性搜索时效率比较高,能够快速返回第一次的搜索结果 |
当缺乏索引或者索引条件模糊时,排序合并链接比嵌套循环有效 |
当缺乏索引或者索引条件模糊时,哈希链接比嵌套循环有效。通常比排序合并链接快。在数据仓库环境下,如果表的记录数多,效率高 |
缺点 |
当索引丢失或者查询条件限制不够时,效率很低;当表的记录数多时,效率低 |
所有的表都需要排序。它为最优化的吞吐量而设计,并且在结果没有全部找到前不返回数据 |
为建立哈希表,需要大量内存。第一次的结果返回较慢 |
表的连接方式介绍(NESTED LOOP, SORT MERGE JOIN, HASH JOIN ):等您坐沙发呢!