暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

ORACLE的视图合并HINT

原创 张程 2020-05-05
3339

第一章 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;

复制

image.png

可以看到,优化器没有对视图做合并,对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;

复制

image.png

可以看到,通过HINT系统自动将原有的代码块与其直接相连的表做连接,连接后在做原有视图中的分组动作。我们也可以从OUTLINE部分看出优化器的做法:
将SEL$4合并到SEL$3。

image.png

那试想一下,我们还有没有其他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;

复制

image.png

可以看到,优化器确实如我们期望的那样,将视图基表合并到了与之相连的表。
同时通过OUTLINE部分也能印证。

image.png

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;

复制

image.png

可以看到,优化器仍然将视图与直接相连的表做合并,通过OUTLINE也能印证。

image.png

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;

复制

image.png

可以看到,直接通过视图别名也是可以的,甚至当别名与已有代码块重名是,只要定义时通过双引号标识,优化器也是支持该写法的。OUTLINE部分也能印证。

image.png

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;

复制

调整后的执行计划如下:

image.png

可以看到,优化器将原始视图基表合并到对应的代码块SEL$6FFDBC86中,并自行决定分组时机。可以看出合并后也不再出现SEL$6FFDBC86代码块,基表T2、WALX、VALX也都并列存在于新生成的代码块SEL$5B97EA23中。
通过OUTLINE部分印证:

image.png

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;

复制

image.png

可以看到执行计划完全相同,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;

复制

image.png

可以看到执行计划均保持一致:

image.png

第三章 总结

通过上述不同类型的场景测试可知,发生视图合并时也是可以通过HINT来控制对应视图合并到的部分的,如:与直接关联的表连接合并、或人为指定合并到的层级代码块。当我们实际在优化SQL并利用到视图合并的处理手段时,可以通过对应HINT进行优化调整。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

目录
  • 第一章 ORACLE的视图合并介绍
    • 1.1 视图合并
    • 1.2 常见的视图合并HINT
  • 第二章 实验环境测试
    • 2.1 原始效果
    • 2.2 与直接关联的表连接合并分组
      • 2.2.1 MERGE(@”待合并代码块”)
      • 2.2.2 MERGE(@”待合并代码块” >”关联表代码块”)
      • 2.2.3 视图中MERGE
      • 2.2.4 MERGE(”视图别名”)
    • 2.3 人为指定合并分组层级
      • 2.3.1 MERGE(@”待合并代码块” >”合并到代码块”)
      • 2.3.2 视图中MERGE(>”合并到代码块”)
      • 2.3.3 MERGE(”视图别名” >”合并到代码块”)
  • 第三章 总结