1.1.1 "或"扩张
“或”扩张(or
expansion)的目的是将查询的WHERE条件中包含分隔谓词的语句转化为使用一个或多个UNION ALL集合运算符的复合查询。通常情况下,每个分隔的谓词被转化成为一个组件查询。这里应用的是一个基于成本的查询转换,大多数时候是为了启用额外的索引访问路径。实际上,分隔的谓词和索引在一起搭配时并不总是进展顺利。还要注意仅从11.2.0.2版本起这种查询转换才开始支持函数式索引。
注意 即使“或”扩张是基于成本的查询转换,查询优化器也会在尝试使用它之前检查一些启发式查询转换。如果查询转换不被允许,可能会错失一个拥有更低成本的执行计划。
接下来的例子基于or_expansion.sql这个脚本,注意,WHERE条件包含两个分隔的谓词。因为这些原因,查询优化器会评估一次基于表扫描的成本是否高于两次单独的基于索引扫描的成本:
SELECT pad FROM t WHERE n1 = 1 or n2=2;
如果两次索引扫描的成本更低,“或”扩张就会产生以下查询。注意,添加lnnvl(n1=1)这个谓词是为了避免多重记录。lnnvl函数在作为参数传递的条件为FALSE或NULL时返回TRUE。因此,第二个组件查询只会在第一个组件查询未返回某条记录的情况下才返回这条记录:
SELECT pad FROM t WHERE n1 = 1
UNION ALL
SELECT pad FROM t WHERE n2 =2 AND
lnnvl(n1=1);
一些分隔的谓词永远不会被显式地用“或”扩张转换。下面的查询就展示了这样一个例子。所有的谓词都引用了一个叫作n1的列,使得WHERE条件的内容能够像IN条件那样处理:
SELECT * FROM t WHERE n1=1 or n1=2 or n1=3
or n1=4;
1.1.2 视图合并
视图合并(View Merging)的目的是通过合并语句中一部分视图和内联视图,以便减少由它们产生的查询块的数量。引入这个查询转换的原因是,如果没有它,查询优化器就会分别处理每一个查询块。当分别处理每个查询块时,查询优化器无法保证每次都为整体SQL语句产生最优的执行计划。此外,由视图合并产生的查询块可能会进一步引导启用其他的查询转换。
查询块
简单地说,最顶级的SQL语句以及一个SQL语句中拥有自己的SELECT子句的每个扩展部分都是查询块。简单的SQL语句只有一个单独的查询块。而一旦使用了视图或者像子查询、内联视图以及集合运算符这样的结构,多重的查询块就出现了。例如,下面的查询有两个查询块(为了阐明问题,我用子查询分解子句代替定义一个真正的视图)。第一个查询块是顶层查询,就是引用dept表的那个查询。第二个查询块是使用WITH子句定义的查询,它引用的是emp表:
WITH emps
AS(SELECT deptno, count(*) AS cnt
FROM emp
GROUP BY
deptno)
SELECT
dept.dname, emps.cnt
FROM
dept,emps
WHERE
dept.deptno = emps.deptno;
视图合并有两个子范畴。
Ø 简单视图合并(Simple view merging)用于合并简单的选择-投影-连接查询块。因为它所处理情况的简单性,简单视图合并是一种基于启发式的查询转换。它无法应用于包含类似聚合、集合运算符、层次查询、MODEL子句或者SELECT列表中含有子查询这样的视图或内联视图。
Ø 复杂视图合并(Complex view merging)用于合并包含聚合的查询块。这是一种基于成本的查询转换,无法应用于有层次查询出现或者包含GROUPING SETS、ROLLUP、PIVOT或者MODEL子句的视图或内联视图。
注意,因为应用复杂视图合并不一定能够带来好处,所以它是基于成本的查询转换。实际上,应用它时,物化视图或者内联视图中出现的聚合就被推后了,因此可能导致SQL在一个很大的结果集上执行。
选择-投影-连接查询块由三个基本操作组成:
一个选择操作用于抽取满足指定谓词的记录, 一个投影操作从引用的表中抽取指定的列,还有一个连接操作将不同表中抽取的数据放在一起。过滤和连接谓词基于类似等号这样的简单运算符。例如: SELECT ti.id, t2.n FROM t1 JOIN t2 ON t1.id =t2.id WHERE t1.n =42。
视图合并可能会带来安全问题。为了预防这些问题,就提出了安全视图合并的概念,并由初始化参数optimizer_secure_view_merging控制其是否可用。
1.简单视图合并
在下面这个来自simple_view_merging.sql脚本的例子中,查询由三个查询块构成:顶层查询和两个内联视图。注意,这两个内联视图是简单的选择-投影-连接查询块:
SELECT *
FROM (SELECT t1.* FROM t1, t2 WHERE t1.id =
t2.t1_id) t12,
(SELECT * FROM t3 WHERE id > 6) t3
WHERE t12.id = t3.t1_id;
因为内联视图可以进行合并,简单视图合并产生了以下查询:
SELECT t1.*,t3.*
FROM t1, t2,t3
WHERE t1.id = t3.t1_id and t1.id = t2.t1_id
and t3.id > 6;
当涉及外链接时简单视图合并就不一定每次都能执行了。例如,在之前的查询中,如果把顶层查询的谓词改成t12.id=t3.t1_id(+), 视图合并仍可以执行,但是如果将谓词改成t12.id(+)=t3.t1_id 就没法执行视图合并了。
2.复杂视图合并
下面的例子来自complex_view_merging.sql脚本,展示了一个带有CROUP BY子句的内联视图。这样的查询按以下方式执行:访问内联视图中引用的表,评估GROUPBY子句和sum函数,最后将内联视图的结果集与顶层查询引用的表进行连接:
SELECT t1.* t2.sum_n
FROM t1, (SELECT n, sum(n) AS sum_n
FROM t2
GROUP BY n) t2
WHERE t1.n = t2.n;
将GROUP BY子句的评估推迟直到连接完毕之后有利时,复杂视图合并产生以下查询:
SELECT t1.*, t2.sum(n) AS sum_n
FROM t1,t2
WHERE t1.n = t2.n
GROUP BY t1.id,t1.n,t1.pad,t1.rowid,t2.n;




