《基于 Oracle 的 SQL 优化》笔记 第四章 Oracle 里的查询转换

查询转换(Query Transformation)又称为查询改写(Query Rewrite),是指 Oracle 在解析目标 SQL 时可能会对其做等价改写,目的是为了生成更高效地执行目标 SQL,即 Orace 可能会将目标 SQL 改写成语义上完全相等但执行效率更高的形式。

子查询展开

子查询展开(subquery unnesting)是指 Oracle 不再将目标 SQL 中的子查询当作一个独立的处理单元来单独执行,而是将该子查询转换为它自身和外部查询之间等价的表连接。这种等价表连接转换要么是将子查询拆开(即将该子查询中的表、视图从子查询中拿出来,然后和外部查询中的表、视图做表连接),要么是不拆开但是会把子查询转换为一个内嵌视图(inline view),然后再和外部查询中的表、视图做表连接。

在 Oracle 10g 及其以后的版本中,对于那种不拆开子查询但是会把该子查询转换成一个内嵌视图的子查询展开,只有当经过子查询展开后的等价改写 SQL 的成本值小于原 SQL 的成本值时,Oracle 才会对原 SQL 执行子查询展开。

子查询展开通常都会提高原 SQL 的执行效率,因为如果原 SQL 不做子查询展开,那么通常情况下,该子查询就会在其执行计划的最后一步才被执行,并且会走 FILTER 类型的执行计划,这样就意味着对于外部查询所在结果集中的每一条记录,该子查询都会被当作一个独立的执行单元来执行一次,外部查询所在的结果集有多少条记录,该子查询就会被执行多少次(近似这样,实际上不完全是这样)。

Oracle 数据库里子查询前的 where 条件如果是如下这些条件之一,那么这种类型的目标 SQL 在满足了一定的条件后就可以做子查询展开:

  • SINGLE_ROW(即 =, <, >, <=, >=, <>),意味着子查询的返回结果至多只会有一条记录。
  • 可能返回多条记录的子查询:EXISTS, NOT EXISTS, IN, NOT IN, ANY, ALL

连接谓词推入(Join Predication Pushdown)

连接谓词推入是优化器处理带视图的目标 SQL 的另外一种优化手段,是指虽然优化器还是会把该 SQL 中视图定义 SQL 语句当作一个独立的处理单元来执行,但此时优化器会把原本处于视图外部查询中和该视图之间的连接条件推入到该视图的定义 SQL 语句内部,这样做是为了能使用上该视图内部相关基本表上的索引,进而能走出基于索引的嵌套循环连接。

做了连接谓词推入后,原目标 SQL 中的视图就和外部查询产生了关联,同时 Oracle 又必须将该视图的定义 SQL 语句当作一个独立的处理单元来单独执行,这就意味着对于外部查询所在结果集中的每一条记录,上述视图的定义 SQL 语句都得单独执行一次,这样一旦外部查询所在结果集的 Cardinality 比较大的话,即便在执行视图定义 SQL 语句时能用上索引,整个 SQL 的执行效率也不一定会比不做连接谓词推入时的哈希连接或排序连接高。

Oracle 在做连接谓词推入时会考虑成本,只有当经过连接谓词推入后走嵌套循环连接的等价改写 SQL 的成本值小于原 SQL 的成本值时,才会对目标 SQL 做连接谓词推入。

Oracle 能否做连接谓词推入与目标视图的类型、该视图与外部查询之间的连接类型以及连接方法有关。Oracle 仅仅支持对如下类型的视图做连接谓词推入:

  • 视图定义 SQL 语句中包含 UNION ALL , UNION , DISTINCT , GROUP BY 的视图。
  • 和外部查询之间的连接类型是 外连接、反连接、半连接 的视图。

连接谓词推入的执行计划一般含有关键字 PUSHED PREDICATE

连接因式分解(Join Factorization)

连接因式分解是优化器处理带 UNION ALL 的目标 SQL 的一种优化手段。是指优化器在处理以 UNION ALL 连接的目标 SQL 的各个分支时,不再原封不动地分别重复执行每个分支,而是会把各个分支公共的部分提出来作为一个单独的结果集,然后再和原 UNION ALL 中剩下的部分做表连接。

表扩展(Table Expansion)

表扩展是优化器处理针对分区表的目标 SQL 的一种优化手段,是指当目标 SQL 中分区表的某个局部分区由于某种原因在某些分区表上变得不可用(索引状态为 UNUSABLE)时,Oracle 能将原目标 SQL 等价改写为按分区 UNION ALL 的形式,这样除了那些不可用分区所对应的 UNION ALL 分支外,其他分区所对应的 UNION ALL 分支还是可以正常使用该局部分区索引。

表移除(Table Elimination)

表移除是优化器处理带多表连接的 SQL 的一种优化手段,是指优化器会把虽然在目标 SQL 中存在,但是其存在与否对最终执行结果没有影响的表从该目标 SQL 中移除,这样优化器至少可以少做一次表连接,进而就提高了原目标 SQL 的执行效率。

表移除通常应用于那些表与表之间通过外键关联或者表与表之间是外连接的情形。

IN 语句

在 Oracle 数据库里,IN 和 OR 是等价的,优化器在处理带 IN 的目标 SQL 时实际上会将其转换为带 OR 的等价改写 SQL。

优化器在处理带 IN 的目标 SQL 时,通常会采用如下四种方法:

  1. 使用 IN_List Iterator:IN 后面是常量集合,IN 所在的列上必须有索引。
  2. 使用 IN_List Expansion:每个常量提取出来形成一个分支,各分支间用 UNION ALL 连接,是基于成本的,只有改写后的 SQL 的成本更小时才会采用。
  3. 使用 IN_List Filter:IN 后面是子查询,把子查询的结果集当作过滤条件,走 Filter。
  4. 对 IN 做子查询展开,或者既做子查询展开又做视图合并。IN 后面是子查询且能做子查询展开。

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据