当前位置:Gxlcms > 数据库问题 > Oracle优化器的基础知识(二)

Oracle优化器的基础知识(二)

时间:2021-07-01 10:21:17 帮助过:12人阅读

表连接

      顾名思义,表连接就是指多个表之间用连接条件连接在一起,使用表连接的目标SQL的目的就是从多个表获取存储在这些表中的不同维度的数据。体现在SQL语句上,含表连接的目标SQLfrom部分会出现多个表,而这些SQLwhere条件部分则会定义具体的表连接条件。

      当优化器解析含表连接的目标SQL时,它除了会根据目标SQLSQL文本的写法来决定表连接的类型之外,还必须决定如下三件事情才能得到最终的执行计划。

1.表连接顺序

      不管目标SQL中有多少个表做表连接,Oracle在实际执行该SQL时都只能先两两做表连接,再依次执行这样的两两表连接过程,直到目标SQL中所有的表都已连接完毕。从严格意义上来说,这里的表连接顺序包含两层含义:一层含义是当两个表做表连接时,优化器需要决定这两个表中谁是驱动表(outer table),谁是被驱动表(inner table);另外一层含义是当多表(超过两个以上的表)做表连接时,优化器需要决定这些表中谁和谁先做表连接,然后决定这个表连接结果所在的结果集再和剩余表中的哪一个再做表连接,这个两两表连接的过程会一直持续下去,直到目标SQL中所有的表都已经连接完为止。

2.表连接方法

      Oracle数据库中,两个表之间的表连接方法有排序合并(sort merge join)、嵌套循环连接(nested loops join)、哈希连接(hash join)和笛卡儿连接(cross join)4种,所以优化器在解析含表连接的目标SQL时,都需要从上述四种方法中选择一种,作为每一对表两两做表连接时所需要采用的方法。

3.访问单表的方法

      对于优化器而言,仅决定表连接顺序和表连接方法是不够的,这还不中以得到目标SQL的最终执行计划,因为优化器在对目标SQL中的各个表两两做表连接时,还必须决定如何去获取存储在这些表里的不同维度的数据,即优化器还要决定访问单表的方法。比如在访问某个单表时,是采用全表扫描还是走索引,如果是走索引,应该采用什么样的索引访问方法等。

1.1 表连接的类型

通常情况下,我们可以认为Oracle数据库中的表连接分为内连接和外连接这两种类型,表连接的类型会直接决定表连接的结果,而目标SQLSQL文本的写法又直接决定了表连接的类型。

1.1.1 内连接

内连接(Inner Join)是指表连接的结果只包含那些完全满足连接条件的记录。对于包含表连接的目标SQL而言,只要其where条件中没有写那些标准SQL中定义或者Oracle中自定义的表示外连接的关键字(比如标准SQL中的left outer joinright outer joinfull outer join,或者Oracle中自定义的用来表示外连接的关键字“(+)”),则该SQL的连接类型就是内连接。

Oracle自定义的内连接写法:

      目标表1,目标表2 where 连接条件

标准SQL中内连接是用JOIN ON或者JOIN USING

   JOIN ON的语法:

      目标表1 join 目标表2 on (连接条件)

   JOIN USING的语法:

      目标表1 join 目标表2 using(连接列集合)

对于使用JOIN USING的目标SQL而言,如果有多个连接列,其语法中“(连接列集合)”里的各个连接列之间应使用逗号来分隔。需要注意的时,使用JOIN USING的连接语法,如果连接列同时又出现在查询列中,则该连接列前不能带上表名或者表名的别名(alias),否则Oracle会报错(ORA-25154)

标准SQL中还有一种特殊的JOIN USING,我们称之为NATURAL JOIN,其含义是使用NATURAL JOIN的表连接的连接列是表连接的两个表所有的同名列。语法:

      目标表1 natural join 目标表2

这实际相当于目标表1 join 目标表2 using(目标表1和目标表2的所有同名列集合)。使用NATURAL JOIN好外是无须写连接列集合,但其坏处是增加了表连接的执行结果出错的风险,因为两个表之间的同名列不一定在含义上就完全相同,也许只是恰好同名,而即使含义相同,也不一定就需要将它们作为连接列。

1.1.2 外连接

      外连接(Outer Join)是对内连接的一种扩展,它是指表连接的连接结果除了包含那些完全满足连接条件的记录之外还会包含驱动表中所有不满足该条件的记录。

      标准SQL中的外连接分为左外连接(Left Outer Join)、右连接(Right Outer Join)和全连接(Full Outer Join)这三种,它们在标准SQL中对应的关键字分别为left outer joinright outer joinfull outer join,都可以和JOIN ON JOIN USING连用。

左连接的语法:

      目标表1 left outer join 目标表2 on(连接条件) 或目标表1 left outer join 目标表2 using (连接列集合)

      其含义是目标表1和目标表2按括号中的连接条件来做表连接,位于关键字左边的表1作为驱动表(outer table),此时的连接结果包含了表1和表2中所有满足该连接条件的记录外,还会包含驱动表(1)中所有不满足该连接条件的记录,同时,驱动表中所有不满足该连接条件的记录所对应的被驱动表(2)中的查询列均会以NULL值来填充。

右连接的语法:

      目标表1 right outer join 目标表2 on(连接条件) 或目标表1 right outer join 目标表2 using (连接列集合)

      含义与左连接相似,不过,这次位于关键字右表的表2为驱动表。

全连接语法:

      目标表1 full outer join 目标表2 on(连接条件) 或目标表1 full outer join 目标表2 using (连接列集合)

      其含义是目标表1和目标表2按括号中的连接条件来做表连接。此时的连接结果除了包含表1和表2中所有满足该连接条件的记录外,还会包含目标表1和目标表2中所有不满足该连接条件的记录,同时,表1和表2中所有不满足该连接条件的记录所对就的另外一个表中的查询列均会以NULL值来填充。

      上面介绍的范例SQL中除了带连接条件外,并没有带其他额外的限制条件。如果目标SQL中除了表连接条件之外还带了额外的限制条件,则目标SQL中表连接的类型和该额外限制条件在目标SQLSQL文本中出现的位置都可能会对最终执行结果产生影响。

      内连接添加其他限制条件实例:

技术分享

      对内连接而言,除了表连接条件之外的额外限制条件在目标SQLSQL文本中所处的位置不会影响该SQL的实际执行结果。

      外连接添加其他限制条件实例:

技术分享

      对于外连接而言,如果额外限制条件在外连接关键字对应的括号内,这表示该限制条件会在表t1和表t2做右连接之前就被应用在表t1上,而如果额外限制条件在外连接关键字对应的括号外,表示该限制条件在表t1和表t2做完右连接后,才会被应用在表t1和表t2的连接结果集上。

      所以,对于外连接而言,除了表连接条件之外的额外限制条件在目标SQLSQL文本中所处的位置确实可能会影响该SQL的实际执行结果。

      和标准SQL里表示外连接的语法不同,Oracle用自定义的关键字“(+)”来表示外连接。关键字“(+)”的位置在目标SQL连接条件中某一个表的连接列后面,其含义是关键字“(+)”出现在哪个表的连接列后面,就表明哪个表会以NULL值来填充那不满足连接条件找位置该表中的查询列,此时应该以关键字“(+)”对应的表作为外连接的驱动表,这是的关键是哪个表是驱动表!

      之前提到过:对于外连接而言,表连接条件之外的额外限制条件在目标SQLSQL文本中所处位置的不同可能会影响该SQL的实际执行结果。那如果使用Oracle自定义的关键字“(+)”来表示外连接的话,那么如何体现呢?很简单,Oracle是通过在额外限制条件的目标列的后面带上同样的关键字“(+)”来体现出上述影响的:

select t1.col1,t1.col2,t2.col3

from t1,t2

where t1.col2(+)=t2.col2

and t1.col1(+)=1;

      前面提到的NATURAL JOIN不仅适用于内连接,也同样适用于外连接:

select t1.col1,col2,t2.col3

from t1 natural left outer join t2 ;

1.2 表连接的方法

      之前介绍过,优化器在解析含表连接的目标SQL时,当它根据目标SQLSQL文本的写法决定表连接的类型之后,接下来要做的事情之一就是决定表连接的方法。

      Oracle数据库中,两个表之间的表连接方法有排序合并连接、嵌套循环连接、哈希连接和笛卡儿连接这四种。这四种表连接各有优缺点,也各有其适用场景,接下来分别介绍它们

1.2.1 排序合并连接

      排序合并连接(Sort Merge Join)是一种两个表在做表连接时用排序操作(Sort)和合并操作(Merge)来得到连接结果集的表连接方法。

      如果两个表(假如为T1T2)做表连接时使用的是排序合并连接,则Oracle会依次顺序执行如下步骤:

  1. 首先以目标SQL中指定的谓词条件(如果有的话)去访问表T1,然后对访问结果按照表T1中的连接来排序,排好序后的结果集我们记为结果集1

  2. 接着以目标SQL中指定的谓词条件(如果有的话)去访问表T2,然后对访问结果按照表T2中的连接来排序,排好序后的结果集我们记为结果集2

  3. 最后对结果集1和结果集2执行合并操作,从中取出匹配记录来作为排序合并连接的最终执行结果。

      对于排序合并连接的优缺点及适用场景,总结如下:

  • 通常情况下,排序合并连接的执行效率会远不如哈希连接,但前者的使用范围更广,因为哈希连接通常只能用于等值连接,而排序合并连接并不能用于其他条件(例如<<=>>=)

  • 通常情况下,排序合并连接并不短途OLTP类型的系统,其本质原因是因为对OLTP类型的系统而言,排序是非常昂贵的操作,当然,如果能避免排序操作,那么即使是OLTP类型的系统,也还是可以使用排序合并连接的。比如两个表虽然是排序合并连接,但实际上它们并不需要排序,因为这两个表各自的连接列上都存在索引。

  • 从严格意义上说,排序合并连接不存在驱动表的概念。

1.2.2 嵌套循环连接

      嵌套循环连接(Nested Loops Join)是一种两个表在做表连接时依靠两层嵌套循环(分别为外层循环和内层循环)来得到连接结果集的表连接方法。

      如果两个表(假如为T1T2)在做表连接时使用的是嵌套循环连接,则Oracle会依次顺序执行如下步骤:

  1. 首先,优化器会按照一定的规则来决定表T1T2中谁是驱动表、谁是被驱动表。驱动表用于外层循环,被驱动表用于内存循环。这是假设驱动表是T1,被驱动表是T2

  2. 接着以目标SQL中指定的谓词条件(如果有的话)去访问驱动表T1,访问驱动表T1后得到的结果集我们记为驱动结果集1

  3. 然后遍历驱动结果集1并同时遍历被驱动表T2,即先取出驱动结果集1中的第1条记录,接着遍历被驱动表T2并按照连接条件去判断T2中是否存在匹配的记录,然后再取出驱动结果集1中的第2条记录,按照同样的连接条件再去遍历被驱动表T2并判断T2中是否还存在匹配的记录,直到遍历完驱动结果集1中所有的记录为止。这里的外层循环是指遍历驱动结果集1所对应的循环,内层循环是指遍历被驱动表T2所对应的循环。显然,外层循环所对应的驱动结果集1有多少条记录,遍历被驱动表T2的内层循环就要做多少次,这就是所谓的“嵌套循环”的含义。

      嵌套循环连接的优缺点及适用场景总结如下:

  • 从上述嵌套循环连接的具体执行过程可以看出:如果驱动表所对应的驱动结果集的记录数较少,同时被驱动表的连接列上又存在唯一性索引(或者在被驱动表的连接列上存在选择性很的的非唯一性索引),那么此时使用嵌套循环连接的执行效率就会非常高;但如果驱动表所对应的驱动结果集的记录数很多,即便在被驱动表的连接列上存在索引,此时使用嵌套循环连接的执行效率也不会高。

  • 只要驱动结果集的记录数较少,那就具备了做嵌套循环连接的前提条件,而驱动结果集是在对驱动表应用了目标SQL中指定的谓词条件(如果有的话)后所得到的结果集,所以大表也可以作为嵌套循环连接的驱动表,关键看目标SQL中指定的谓词条件(如果有的话)能否将驱动结果集的数据量降下来。

  • 嵌套循环连接有其他连接方法所没有的一个优点:嵌套循环连接可以实现快速响应,即它可以第一时间返回已经连接过具满足连接条件的记录,而不必等待所有的连接操作全部做完才返回连接结果。虽然排序合并连接也可以,但它们并不是第一时间返回,因为排序合并连接要等到排序完后做合并操作时才能开始返回数据,而哈希连接则要等到驱动结果集所对应的Hash Table全部建完后才能开始返回数据。

      如果Oracle使用的是嵌套循环连接,且在被驱动表的连接列上存在索引,那么Oracle在访问索引时通常会使用单块读,这意味着嵌套循环连接的驱动结果集有多少条记录,Oracle就会需要访问该索引多少次。另外,如果目标SQL中的查询列并不能全部从驱动表的相关索引中获得,那么Oracle在做完嵌套循环连接后还需要对被驱动表执行回表操作。这个回青操作通常也会使用单块读,这意味着做完嵌套循环连接后的连接结果集有多少条记录,Oracle就需要回表多少次。

      为了提高嵌套循环连接的执行效率,在Oracle 11g中,Oracle引入了向量I/O(Vector I/O)。在引入向量I/O后,Oracle就可以将原先一批单块读所需要耗费的物理I/O结合起来,然后用一个向量I/O去批处理它们,这样就实现了在单块读的数量不降低的情况下减少这些单块读所需要耗费的物理I/O数量,也就提高了嵌套循环连接的执行效率。

1.2.3 哈希连接

      哈希连接(Hash Join)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的表连接方法。

      Oracle7.3之前,Oracle数据库中常用的表连接方法就只有排序合并连接和嵌套循环连接这两种,但这两种方法都各有其明显缺陷。对于排序合并连接,如果两个表在施加了目标SQL中指定的谓词条件(如果有的话)后得到的结果集很大且需要排序,则排序合并连接的执行效率一定不高;而对于嵌套循环连接,如果驱动表所对应的驱动结果集的记录数很大,即便在被驱动表的连接列上存在索引,此时使用嵌套循环连接的

人气教程排行