第一章 ORACLE的视图合并介绍
1.1 视图合并
首先介绍一下ORACLE的视图合并概念:
视图合并(View Merging)是优化器处理带视图的目标SQL的一种优化手段,它是指优化器不再将目标SQL中视图的定义语句当做一个独立的处理单元来执行,而是会将其拆开,把其定义SQL语句的基表拿出来与外表合并,合并后的SQL将只剩下外部查询的表和原视图中的基表,不再有视图出现。
视图合并的好处是让优化器有更多的执行路径选择,而不拘泥于原有SQL的书写结构、表连接等方式。
ORACLE中的视图合并主要包括简单视图合并、外连接视图合并和复杂视图合并。本篇主要讨论视图合并中的HINT用法,对于不同的合并类型不再过多讨论。
1.2 常见的视图合并HINT
众所周知,常见的视图合并HINT包括:
MERGE、NO_MERGE。代表对视图合并/不合并。
那是否还有其他用于调整视图合并方式的HINT呢?
以下用一个复杂视图合并例子来说明:脚本来源于MOS,测试环境ORACLE 12C。
对于复杂视图合并,意味着优化器会推迟视图中的GROUP BY或DISTINCT操作,并比较合并前后的查询成本来觉得是否做复杂视图合并。
表结构如下:
create or replace type tyalx as table of number; / create table talx(ta number, tb number, tc number); create table ualx(ua number); create table valx(va number); create table walx(wa number); begin dbms_stats.gather_table_stats(null,'talx'); dbms_stats.gather_table_stats(null,'ualx'); dbms_stats.gather_table_stats(null,'valx'); dbms_stats.gather_table_stats(null,'walx'); end; /
复制
第二章 实验环境测试
实验脚本如下:
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
2.1 原始效果
首先测试下不做视图合并的效果:
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select /*+ NO_MERGE */ wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到,优化器没有对视图做合并,对WALX表做分组后再与后续的表连接。
此时如果我们希望推迟对WALX表的分组动作,可以有两种思路:
1.与T2(直接关联的表)连接后分组;
2.人为指定合并分组时机。
以下分别对这两种思路做测试:
2.2 与直接关联的表连接合并分组
首先我们通过HINT让优化器对代码块SEL$4做合并。
2.2.1 MERGE(@”待合并代码块”)
select /*+ MERGE(@"SEL$4") */ 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到,通过HINT系统自动将原有的代码块与其直接相连的表做连接,连接后在做原有视图中的分组动作。我们也可以从OUTLINE部分看出优化器的做法:
将SEL$4合并到SEL$3。
那试想一下,我们还有没有其他HINT写法呢,如上面的HINT:MERGE(@“SEL$4” >“SEL$3”)。
指定合并到与之关联表所属的代码块。下面测试:
2.2.2 MERGE(@”待合并代码块” >”关联表代码块”)
select /*+ MERGE(@"SEL$4" >"SEL$3") */ 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到,优化器确实如我们期望的那样,将视图基表合并到了与之相连的表。
同时通过OUTLINE部分也能印证。
2.2.3 视图中MERGE
当我们直接把HINT写到视图中,测试合并效果:
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select /*+ MERGE */ wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到,优化器仍然将视图与直接相连的表做合并,通过OUTLINE也能印证。
2.2.4 MERGE(”视图别名”)
测试一下直接写视图的别名:
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select /*+ MERGE("W") */ t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1; select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select /*+ MERGE("SEL$3") */ t2.tb from talx t2 inner join (select wa from walx group by wa) "SEL$3" on "SEL$3".wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到,直接通过视图别名也是可以的,甚至当别名与已有代码块重名是,只要定义时通过双引号标识,优化器也是支持该写法的。OUTLINE部分也能印证。
2.3 人为指定合并分组层级
我们也可以指定优化器对视图合并到哪部分代码块。
2.3.1 MERGE(@”待合并代码块” >”合并到代码块”)
可以人为指定将视图所属的代码块合并到目标代码块中。
select /*+ MERGE(@"SEL$4" >"SEL$6FFDBC86") */ 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
调整后的执行计划如下:
可以看到,优化器将原始视图基表合并到对应的代码块SEL$6FFDBC86中,并自行决定分组时机。可以看出合并后也不再出现SEL$6FFDBC86代码块,基表T2、WALX、VALX也都并列存在于新生成的代码块SEL$5B97EA23中。
通过OUTLINE部分印证:
2.3.2 视图中MERGE(>”合并到代码块”)
也可以将HINT写进视图中,并指定合并到的代码块部分
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select t2.tb from talx t2 inner join (select /*+ MERGE(>"SEL$6FFDBC86") */ wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到执行计划完全相同,MERGE(@“SEL$4” >“SEL$6FFDBC86”)。
2.3.3 MERGE(”视图别名” >”合并到代码块”)
select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select /*+ MERGE("W" >"SEL$6FFDBC86") */ t2.tb from talx t2 inner join (select wa from walx group by wa) w on w.wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1; select 1 from talx t1 inner join ualx on ua = t1.ta left outer join (select /*+ MERGE("SEL$3" >"SEL$6FFDBC86") */ t2.tb from talx t2 inner join (select wa from walx group by wa) "SEL$3" on "SEL$3".wa = t2.tc inner join valx on va = 'Y') v on v.tb = t1.tb left outer join table (tyalx(1)) t on 1=1;
复制
可以看到执行计划均保持一致:
第三章 总结
通过上述不同类型的场景测试可知,发生视图合并时也是可以通过HINT来控制对应视图合并到的部分的,如:与直接关联的表连接合并、或人为指定合并到的层级代码块。当我们实际在优化SQL并利用到视图合并的处理手段时,可以通过对应HINT进行优化调整。