表的连接是指在一个SQL语句中通过表与表之间的关联,从一个或多个表检索出相关的数据。连接是通过SQL语句中FROM从句的多个表名,以及WHERE从句里定义的表之间的连接条件来实现的。如果一个SQL语句的关联表超过两个,那么连接的顺序如何呢?
ORACLE首先连接其中的两个表,产生一个结果集;然后将产生的结果集与下一个表再进行关联;继续这个过程,直到所有的表都连接完成;最后产生所需的数据。
介绍一下以下三种常用的表的连接方式:
(一)嵌套循环连接(NESTED LOOP JOIN)
(二)排序合并连接(SORT MERGE JOIN)
(三)哈希连接(HASH JOIN)。
1、嵌套循环连接(NESTED LOOP JOIN) nested loop join
嵌套循环连接的内部处理的流程:
1)Oracle优化器根据基于规则RBO或基于成本CBO的原则,选择两个表中的一个作为驱动表,并指定其为外部表。
2)Oracle优化器再将另外一个表指定为内部表。
3)Oracle从外部表中读取第一行,然后和内部表中的数据逐一进行对比,所有匹配的记录放在结果集中。
4)Oracle读取外部表中的第二行,再和内部表中的数据逐一进行对比,所有匹配的记录添加到结果集中。
5)重复上述步骤,直到外部表中的所有纪录全部处理完。
6)最后产生满足要求的结果集。
通过查询SQL语句的执行计划可以看出哪个表是外部表,哪个为内部表。
使用嵌套循环连接是一种从结果集中提取第一批记录最快速的方法。在驱动行源表(就是正在查找的记录)较小、或者内部行源表已连接的列有惟一的索引或高度可选的非惟一索引时,嵌套循环连接效果是比较理想的。嵌套循环连接比其他连接方法有优势,它可以快速地从结果集中提取第一批记录,而不用等待整个结果集完全确定下来。这样,在理想情况下,终端用户就可以通过查询屏幕查看第一批记录,而在同时读取其他记录。不管如何定义连接的条件或者模式,任何两行记录源可以使用嵌套循环连接,所以嵌套循环连接是非常灵活的。
嵌套循环不适用的地方:
然而,如果内部行源表(读取的第二张表)已连接的列上不包含索引,或者索引不是高度可选时,嵌套循环连接效率是很低的。如果驱动表的记录非常庞大时,其他的连接方法可能更加有效。
可以通过在SQL语句中添加HINTS,强制ORACLE优化器产生嵌套循环连接的执行计划。
2、排序合并连接(SORT MERGE JOIN)
排序合并连接内部处理的流程:
1)优化器判断第一个源表是否已经排序,如果已经排序,则到第3步,否则到第2步;
2)第一个源表排序;
3)优化器判断第二个源表是否已经排序,如果已经排序,则到第5步,否则到第4步;
4)第二个源表排序;
5)已经排过序的两个源表进行合并操作,并生成最终的结果集。
在缺乏数据的选择性或者可用的索引时,或者两个源表都过于庞大(所选的数据超过表记录数的5%)时,排序合并连接将比嵌套循环连更加高效。
排列合并连接需要比较大的临时内存块,以用于排序,这将导致在临时表空间占用更多的内存和磁盘I/O。
可以通过在SQL语句中添加HINTS,强制ORACLE优化器产生排序合并连接的执行计划。
select /*+ use_merge(a b) */ a.user_name,b.dev_no
from user_info a, dev_info b
where a.user_id > b.user_id;
排序合并连接是基于RBO的。
3、哈希连接(HASH JOIN)
当内存能够提供足够的空间时,哈希(HASH)连接是Oracle优化器通常的选择。哈希连接中,优化器根据统计信息,首先选择两个表中的小表,在内存中建立这张表的基于连接键的哈希表;优化器再扫描表连接中的大表,将大表中的数据与哈希表进行比较,如果有相关联的数据,则将数据添加到结果集中。
当表连接中的小表能够完全cache到可用内存的时候,哈希连接的效果最佳。哈希连接的成本只是两个表从硬盘读入到内存的成本。
但是,如果哈希表过大而不能全部cache到可用内存时,优化器将会把哈希表分成多个分区,再将分区逐一cache到内存中。当表的分区超过了可用内存时,分区的部分数据就会临时地写到磁盘上的临时表空间上。因此,分区的数据写磁盘时,比较大的区间(EXTENT)会提高I/O性能。ORACLE推荐的临时表空间的区间是1MB。临时表空间的区间大小由UNIFORM. SIZE指定。
当哈希表构建完成后,进行下面的处理:
1)第二个大表进行扫描;
2)如果大表不能完全cache到可用内存的时候,大表同样会分成很多分区;
3)大表的第一个分区cache到内存;
4)对大表第一个分区的数据进行扫描,并与哈希表进行比较,如果有匹配的纪录,添加到结果集里面;
5)与第一个分区一样,其它的分区也类似处理。
6)所有的分区处理完后,ORACLE对产生的结果集进行归并,汇总,产生最终的结果。
当哈希表过大或可用内存有限,哈希表不能完全CACHE到内存。随着满足连接条件的结果集的增加,可用内存会随之下降,这时已经CACHE到内存的数据可能会重新写回到硬盘去。如果出现这种情况,系统的性能就会下降。
当连接的两个表是用等值连接并且表的数据量比较大时,优化器才可能采用哈希连接。哈希连接是基于CBO的。只有在数据库初始化参数HASH_JOIN_ENABLED设为True,并且为参数PGA_AGGREGATE_TARGET设置了一个足够大的值的时候,Oracle才会使用哈希边连接。HASH_AREA_SIZE是向下兼容的参数,但在Oracle9i之前的版本中应当使用HASH_AREA_SIZE。当使用ORDERED提示时,FROM子句中的第一张表将用于建立哈希表。
Create table a as select * from emp
Create table b as select * from dept;
可以通过在SQL语句中添加HINTS,强制ORACLE优化器产生哈希连接的执行计划。
select /+ use_hash(a b)/ a.user_name,b.dev_no
from user_info a, dev_info b
where a.user_id = b.user_id;
当缺少有用的索引时,哈希连接比嵌套循环连接更加有效。哈希连接也可能比嵌套循环连接更快,因为处理内存中的哈希表比检索B_树索引更加迅速。
三 、几种主要表连接的比较
深入地理解和掌握oracle的表连接对于优化数据库的性能至关重要。由于优化器选择方式的不同,以及统计信息的缺失或统计信息的不准确,ORACLE自动选择的表连接方式不一定是最优的。当SQL语句的执行效率很低时,可通过auto trace对执行计划进行跟踪和分析。当出现多表连接时,需要仔细分析是否有更佳的连接条件。根据系统的特点,必要时可以在SQL中添加HINTS,从而改变SQL的执行计划,从而达到性能优化的目的。
总结一下,在哪种情况下用哪种连接方法比较好:
排序 - -合并连接(Sort Merge Join, SMJ):
a)对于非等值连接,这种连接方式的效率是比较高的。
b)如果在关联的列上都有索引,效果更好。
c)对于将2个较大的row source做连接,该连接方法比NL连接要好一些。
d)但是如果sort merge返回的row source过大,则又会导致使用过多的rowid在表中查询数据时,数据库性能下降,因为过多的I/O。
嵌套循环(Nested Loops, NL):
a)如果driving row source(外部表)比较小,并且在inner row source(内部表)上有唯一索引,或有高选择性非唯一索引时,使用这种方法可以得到较好的效率。
b) NESTED LOOPS有其它连接方法没有的的一个优点是:可以先返回已经连接的行,而不必等待所有的连接操作处理完才返回数据,这可以实现快速的响应时间。
哈希连接(Hash Join, HJ):
a)这种方法是在Oracle7后来引入的,使用了比较先进的连接理论,一般来说,其效率应该好于其它2种连接,但是这种连接只能用在CBO优化器中,而且需要设置合适的hash_area_size参数,才能取得较好的性能。
b)在2个较大的row source之间连接时会取得相对较好的效率,在一个row source较小时则能取得更好的效率。
c)只能用于等值连接中
原文链接:https://blog.csdn.net/flexes/article/details/10970869