哈希连接(HASH JOIN)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的表连接方法。
对于排序合并连接,如果两个表在施加了目标SQL中指定的谓词条件后得到的结果集很大而且需要排序,则排序合并连接的执行效率一定不高;而对于嵌套循环连接,如果驱动表所对应的驱动结果集的记录数很大,即便在被驱动表的连接列上存在索引,此时使用嵌套循环连接的执行效率也会同样不高。为了解决这个问题,于是Oracle引进了哈希连接。在ORACLE 10g及其以后的版本中,优化器 (实际上是CBO,因为哈希连接仅适用于CBO)在解析目标SQL的时候是否考虑哈希连接受限于隐含参数_HASH_JOIN_ENABLED,默认值是TRUE.
对于哈希连接的优缺点及适用场景如下:
a,哈希连接不一定会排序,或者说大多数情况下都不需要排序
b,哈希连接的驱动表所对应的连接列的选择性尽可能好。
c,哈希只能用于CBO,而且只能用于等值连接的条件。(即使是哈希反连接,ORACLE实际上也是将其换成等值连接)。
c,哈希连接很适用小表和大表之间做连接且连接结果集的记录数较多的情形,特别是小表的选择性非常好的情况下,这个时候哈希连接的执行时间就可以近似看做和全表扫描个个大表的费用时间相当。
e,当两个哈希连接的时候,如果在施加了目标SQL中指定的谓词条件后得到的数据量较小的那个结果集所对应的HASH TABLE能够完全被容纳在内存中(PGA的工作区),此时的哈希连接的执行效率非常高。
oracle表之间的连接之哈希连接(Hash Join),其特点如下:
1,驱动表和被驱动表都是最多只被访问一次。
2,哈希连接的表有驱动顺序。
3,哈希表连接的表无需要排序,但是他在做连接之前做哈希运算的时候,会用到HASH_AREA_SIZE来创建哈希表。
4,哈希连接不适用于的连接条件是:不等于<>,大于>,小于<,小于等于<=,大于等于>=,like。
5,哈希连接索引列在表连接中无特殊要求,与单表情况无异。
************************************************************************************************
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for
Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
-
--use_nl,use_hash,use_merge的3种连接方式驱动与被驱动关系
-
-
一.HASH连接方式
-
-
--建一个小表和一个大一些的表
-
SQL> create table t1 as select * from dba_objects where rownum<11;
-
-
Table created.
-
-
SQL> create table t2 as select * from dba_objects;
-
-
Table created.
-
-
SQL> select count(1) from t1;
-
-
COUNT(1)
-
----------
-
10
-
-
SQL> select count(1) from t2;
-
-
COUNT(1)
-
----------
-
75211
-
-
--试验HASH,可以看出hash是存在驱动与被驱动关系,驱动表适合于两表间较小的一个,耗用更少的排序开销;
-
SQL> select /*+ leading(t2) use_hash(t2,t1) */t1.status,t2.status
-
2 from t1,t2
-
3 where t1.object_id=t2.object_id;
-
-
STATUS STATUS
-
------- -------
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
-
10 rows selected.
-
-
--看到排序区中排序的开销为3439K;
-
SQL> @allstat
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 315bd4pgdyt86, child number 1
-
-------------------------------------
-
select /*+ leading(t2) use_hash(t2,t1) */t1.status,t2.status from t1,t2
-
where t1.object_id=t2.object_id
-
-
Plan hash value: 2959412835
-
-
-------------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
-
-------------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.08 | 1078 | 1071 | | | |
-
|* 1 | HASH JOIN | | 1 | 10 | 10 |00:00:00.08 | 1078 | 1071 | 3439K| 1573K| 5617K (0)|
-
| 2 | TABLE ACCESS FULL| T2 | 1 | 79628 | 75211 |00:00:00.02 | 1074 | 1071 | | | |
-
| 3 | TABLE ACCESS FULL| T1 | 1 | 10 | 10 |00:00:00.01 | 4 | 0 | | | |
-
-------------------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
-
25 rows selected.
-
-
--而T1表为驱动表时,排序的开销为1206K比上面的3439K小得多;
-
SQL> select /*+ leading(t1) use_hash(t2,t1) */t1.status,t2.status
-
2 from t1,t2
-
3 where t1.object_id=t2.object_id;
-
-
STATUS STATUS
-
------- -------
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
VALID VALID
-
-
10 rows selected.
-
-
SQL> @allstat
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 3zc11bg9gdq31, child number 1
-
-------------------------------------
-
select /*+ leading(t1) use_hash(t2,t1) */t1.status,t2.status from t1,t2
-
where t1.object_id=t2.object_id
-
-
Plan hash value: 1838229974
-
-
-------------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
-
-------------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.08 | 1078 | 1071 | | | |
-
|* 1 | HASH JOIN | | 1 | 10 | 10 |00:00:00.08 | 1078 | 1071 | 1206K| 1206K| 1126K (0)|
-
| 2 | TABLE ACCESS FULL| T1 | 1 | 10 | 10 |00:00:00.01 | 3 | 0 | | | |
-
| 3 | TABLE ACCESS FULL| T2 | 1 | 79628 | 75211 |00:00:00.03 | 1075 | 1071 | | | |
-
-------------------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
-
25 rows selected.
-
-
--而ORACLE在预估COST时第一类的COST也是大于第2类,也就是默认O是会采用第2种方式;
-
--第1类的COST为419,而第2类的COST预估为305;
-
SQL> set auto traceonly
-
SP2-0158: unknown SET autocommit option "traceonly"
-
Usage: SET AUTO[COMMIT] { OFF | ON | IMM[EDIATE] | n }
-
SQL> set autot traceonly exp
-
SQL>
-
SQL> select /*+ leading(t2) use_hash(t2,t1) */t1.status,t2.status
-
2 from t1,t2
-
3 where t1.object_id=t2.object_id;
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2959412835
-
-
-----------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 10 | 360 | | 419 (1)| 00:00:06 |
-
|* 1 | HASH JOIN | | 10 | 360 | 2336K| 419 (1)| 00:00:06 |
-
| 2 | TABLE ACCESS FULL| T2 | 79628 | 1399K| | 301 (1)| 00:00:04 |
-
| 3 | TABLE ACCESS FULL| T1 | 10 | 180 | | 3 (0)| 00:00:01 |
-
-----------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
SQL> select /*+ leading(t1) use_hash(t2,t1) */t1.status,t2.status
-
2 from t1,t2
-
3 where t1.object_id=t2.object_id;
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1838229974
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 10 | 360 | 305 (1)| 00:00:04 |
-
|* 1 | HASH JOIN | | 10 | 360 | 305 (1)| 00:00:04 |
-
| 2 | TABLE ACCESS FULL| T1 | 10 | 180 | 3 (0)| 00:00:01 |
-
| 3 | TABLE ACCESS FULL| T2 | 79628 | 1399K| 301 (1)| 00:00:04 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
--HASH连接方式默认当然是采用后一种COST较小那类;
-
SQL> select t1.status,t2.status
-
2 from t1,t2
-
3 where t1.object_id=t2.object_id;
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1838229974
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 10 | 360 | 305 (1)| 00:00:04 |
-
|* 1 | HASH JOIN | | 10 | 360 | 305 (1)| 00:00:04 |
-
| 2 | TABLE ACCESS FULL| T1 | 10 | 180 | 3 (0)| 00:00:01 |
-
| 3 | TABLE ACCESS FULL| T2 | 79628 | 1399K| 301 (1)| 00:00:04 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
-
二.排序合并连接方式
-
--可以看到无论是哪种表排在前面先执行,其开销都是一样的;也就是说明merge方式是不存在驱动与被驱动的区别关系 ;
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 2yw072zjb9h5b, child number 1
-
-------------------------------------
-
select /*+ leading(t1) use_merge(t2,t1) */t1.status,t2.status from
-
t1,t2 where t1.object_id=t2.object_id
-
-
Plan hash value: 412793182
-
-
--------------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
-
--------------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.06 | 1077 | 1071 | | | |
-
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.06 | 1077 | 1071 | | | |
-
| 2 | SORT JOIN | | 1 | 10 | 10 |00:00:00.01 | 3 | 0 | 2048 | 2048 | 2048 (0)|
-
| 3 | TABLE ACCESS FULL| T1 | 1 | 10 | 10 |00:00:00.01 | 3 | 0 | | | |
-
|* 4 | SORT JOIN | | 10 | 79628 | 10 |00:00:00.06 | 1074 | 1071 | 2675K| 740K| 2377K (0)|
-
| 5 | TABLE ACCESS FULL| T2 | 1 | 79628 | 75211 |00:00:00.02 | 1074 | 1071 | | | |
-
--------------------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
filter("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
-
28 rows selected.
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID csjsgn0hh4dr6, child number 1
-
-------------------------------------
-
select /*+ leading(t2) use_merge(t2,t1) */t1.status,t2.status from
-
t1,t2 where t1.object_id=t2.object_id
-
-
Plan hash value: 1792967693
-
-
--------------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
-
--------------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.09 | 1077 | 1071 | | | |
-
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.09 | 1077 | 1071 | | | |
-
| 2 | SORT JOIN | | 1 | 79628 | 54 |00:00:00.09 | 1074 | 1071 | 2675K| 740K| 2377K (0)|
-
| 3 | TABLE ACCESS FULL| T2 | 1 | 79628 | 75211 |00:00:00.02 | 1074 | 1071 | | | |
-
|* 4 | SORT JOIN | | 54 | 10 | 10 |00:00:00.01 | 3 | 0 | 2048 | 2048 | 2048 (0)|
-
| 5 | TABLE ACCESS FULL| T1 | 1 | 10 | 10 |00:00:00.01 | 3 | 0 | | | |
-
--------------------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
filter("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
三.嵌套循环连接方式是影响最为明显的,buffers数相差了10倍之多;
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 35g7vtpc63s04, child number 0
-
-------------------------------------
-
select /*+ leading(t1) use_nl(t2,t1) */t1.status,t2.status from t1,t2
-
where t1.object_id=t2.object_id
-
-
Plan hash value: 1967407726
-
-
----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-
----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.11 | 10745 | 10710 |
-
| 1 | NESTED LOOPS | | 1 | 10 | 10 |00:00:00.11 | 10745 | 10710 |
-
| 2 | TABLE ACCESS FULL| T1 | 1 | 10 | 10 |00:00:00.01 | 4 | 0 |
-
|* 3 | TABLE ACCESS FULL| T2 | 10 | 1 | 10 |00:00:00.11 | 10741 | 10710 |
-
----------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - filter("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 1a5dw45kqph9p, child number 0
-
-------------------------------------
-
select /*+ leading(t2) use_nl(t2,t1) */t1.status,t2.status from t1,t2
-
where t1.object_id=t2.object_id
-
-
Plan hash value: 4016936828
-
-
----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-
----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.93 | 226K| 1071 |
-
| 1 | NESTED LOOPS | | 1 | 10 | 10 |00:00:00.93 | 226K| 1071 |
-
| 2 | TABLE ACCESS FULL| T2 | 1 | 79628 | 75211 |00:00:00.06 | 1075 | 1071 |
-
|* 3 | TABLE ACCESS FULL| T1 | 75211 | 1 | 10 |00:00:00.74 | 225K| 0 |
-
----------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - filter("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-
-
Note
-
-----
-
- dynamic sampling used for this statement (level=2)
-
-
-
25 rows selected.
-
-
-
小结:从以上可看出,影响大到小的顺序为nl->hash->merge(不影响);
-
应用:日常工作环境中经常可看到,由于nl方式不当而应使用hash的案例,毕竟NL对数据量的大小影响是最为敏感;
-
而ORACLE选择NL方式并没有错,错就错在预估值那里,所以当用explain plan for....,autot等方式看并不能看出问题;
-
当通过running time的统计信息收集然后select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-
就会发现真正的原因是预估值与实际值的问题相差过大导致的;
-
在这种情况下执行计划的绑定是比较实用和有效的;
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29209863/viewspace-2128167/
,如需转载,请注明出处,否则将追究法律责任。