暂无图片
暂无图片
4
暂无图片
暂无图片
暂无图片

ORACLE子查询 导致无法谓词推入的研究

1359

第一章 问题引入分析

在分析客户环境的一条SQL时,发现了无法顺利谓词推入的现象。故此对案例做了进一步模拟及测试。以确定问题原因。数据库版本:ORACLE 11G。

SELECT 。。。 --省略部分
  FROM (SELECT 
               。。。 --省略部分          
FROM t_a a
        UNION ALL
        SELECT 
               。。。 --省略部分          
FROM t_b rc) cc
 WHERE EXISTS (SELECT 1
          FROM t
         WHERE t.case_id = cc.case_id
           AND t.code = 'xxxxxxxxxxxxx');

其中,子查询中的code列具有很好的过滤条件,返回行数很少。且对应视图cc中相关表的关联列case_id也包含索引。在这种情况下,个人理解优化器应该是可以顺利的将外面EXISTS子查询的关联列推入到视图CC内的,从而通过访问CC的索引来避免视图CC中的两次全表扫描访问的。

而实际事与愿违,不论如何添加hint,都无法推入关联条件到CC视图内。总是通过全表扫描访问大表。造成性能问题。也自己检查了满足谓词推入的条件。确定当前场景下理应可以谓词推入:

image.png

抛开连接方式不谈,首先CC视图就是包含UNION/ALL类的,是满足谓词推入的前提条件的。

基于上述问题现象,设计了如下测试脚本:以供后续实验进一步定位问题:

CREATE TABLE SZT.T1CO AS SELECT * FROM DBA_OBJECTS;
CREATE TABLE SZT.T2CO AS SELECT * FROM DBA_OBJECTS;
CREATE TABLE SZT.T1 AS SELECT * FROM DBA_OBJECTS;
CREATE INDEX SZT.IDX_T2CO_ID ON SZT.T2CO(OBJECT_ID);
CREATE INDEX SZT.IDX_T1CO_ID ON SZT.T1CO(OBJECT_ID);
CREATE INDEX SZT.IDX_T1_DATA ON SZT.T1(DATA_OBJECT_ID);

 exec dbms_stats.gather_table_stats(ownname=>'SZT',tabname=>'T1CO');
 exec dbms_stats.gather_table_stats(ownname=>'SZT',tabname=>'T2CO');
 exec dbms_stats.gather_table_stats(ownname=>'SZT',tabname=>'T1');

测试语句:

select count(*)
  from (select object_id, object_name, data_object_id
          from t2co
        union all
        select object_id, object_name, data_object_id
          from t1co) cc
 where exists (select 1
          from t1
         where data_object_id = '46'
           and t1.object_id = cc.object_id);

对应的执行计划输出如下:

image.png

可以看到,尽管T1表预估与实际返回行数仅为1,但对于希望出现的谓词推入到CC视图的动作,仍然没有出现。与原始问题现象保持一致,两次全表访问大表,造成性能问题。

第二章 不同关联写法的实验对比

基于上一章节发现的性能问题,尝试做了几种结构相似的改写,以确定无法谓词推入的问题是否还存在。也设计了可以做谓词推入的三种类似视图。让测试结果更全面。

2.1 内连接写法

由于EXISTS关联子查询只是为了与CC视图做半连接,可以等价的将EXISTS子查询改写为如下类别语句:
t1子查询去重后与原始的exists子查询完全等价。

2.1.1 UNION视图

select count(*)
  from (select object_id, object_name, data_object_id
          from t2co
        union all
        select object_id, object_name, data_object_id
          from t1co) cc,
(select distinct t1.object_id
          from t1
         where data_object_id = '46') t1
 where t1.object_id = cc.object_id ;

执行计划如下:

image.png

由于改写为内连接形式,顺利实现了谓词推入,这才是我们希望看到的高效查询。

2.1.2 DISTINCT视图

将CC视图改写为distinct视图。

select count(*)
  from (select distinct object_id, object_name, data_object_id
          from t1co) cc,
(select distinct t1.object_id
          from t1
         where data_object_id = '46') t1
 where t1.object_id = cc.object_id ;

执行计划如下:

image.png

同样做了谓词推入。

2.1.3 GROUP BY视图

将CC视图改写为group by视图。

select count(*)
  from (select object_id, object_name, data_object_id,COUNT(*) CNT
          from t1co
		  GROUP BY object_id, object_name, data_object_id) cc,
(select distinct t1.object_id
          from t1
         where data_object_id = '46') t1
 where t1.object_id = cc.object_id ;

执行计划如下:

image.png

同样做了谓词推入。

2.2 EXISTS关联子查询

UNION视图上面已经测试过,无法谓词推入,这里不再测试。

2.2.1 DISTINCT视图
将CC视图改写为distinct视图。

select count(*)
  from (
        select  distinct object_id, object_name, data_object_id
          from t1co) cc
 where exists (select 1
          from t1
         where data_object_id = '46'
           and t1.object_id = cc.object_id);

执行计划如下:

image.png
尽管将视图调整为distinct。但谓词推入技术仍没有出现,访问大表造成性能问题。

2.2.2 GROUP BY视图

将CC视图改写为group by视图。

select count(*)
  from (
        select object_id, object_name, data_object_id,count(*)
          from t1co
		  group by object_id, object_name, data_object_id) cc
 where exists (select 1
          from t1
         where data_object_id = '46'
           and t1.object_id = cc.object_id);

执行计划如下:

image.png

尽管将视图调整为group by。但谓词推入技术仍没有出现,访问大表造成性能问题。

2.3 IN子查询

EXISTS子查询是可以等价改写为IN子查询的。这里继续测试。

2.3.1 UNION视图

select count(*)
  from (select object_id, object_name, data_object_id
          from t2co
        union all
        select object_id, object_name, data_object_id
          from t1co) cc
 where cc.object_id IN (select t1.object_id 
          from t1
         where data_object_id = '46');

执行计划如下:

image.png

通过IN子查询写法,仍然无法完成谓词推入。

2.3.2 DISTINCT视图

将CC视图改写为distinct视图。

select count(*)
  from (
        select  distinct object_id, object_name, data_object_id
          from t1co) cc
 where cc.object_id IN (select t1.object_id 
          from t1
         where data_object_id = '46');

执行计划如下:

image.png

尽管将视图调整为distinct。但谓词推入技术仍没有出现,访问大表造成性能问题。

2.3.3 GROUP BY视图

将CC视图改写为group by视图。

select count(*)
  from (
        select object_id, object_name, data_object_id,count(*)
          from t1co
		  group by object_id, object_name, data_object_id) cc
 where cc.object_id IN (select t1.object_id 
          from t1
         where data_object_id = '46');

执行计划如下:

image.png

尽管将视图调整为group by。但谓词推入技术仍没有出现,访问大表造成性能问题。

第三章 问题总结

经过上述三种场景,九个实验的测试过程可知:
只有与视图间是直接连接的写法,可以顺利实现谓词推入。而对于使用了子查询做主要过滤条件的写法,都不能完成谓词推入。这里分析可能优化器在对子查询做子查询展开后,并没有对能否进一步做谓词推入做比较全面的尝试,导致谓词推入失败。

为了进一步分析问题。尝试对UNION视图的查询做了10053分析。
其中不能谓词推入的EXISTS写法,是有如下信息:

image.png

没有发现有效的连接条件;
没有发现可将谓词推入的有效视图。

这里看到优化器也是尝试做了谓词推入的尝试,但没有成功。

而通过连接写法的视图,其10053中是可以顺利找到谓词推入视图的:

image.png

这里看到,成功的找到可以谓词推入的条件,并将谓词推入到了视图内部。

通过本篇的实验,告诉我们在SQL中的主要过滤条件如果在子查询中,如果还希望将连接条件推入到外部视图中去。是无法顺利完成的。因此就需要我们在编写SQL的时候,对这种场景进行分析。避免通过子查询完成主要的过滤条件。

第四章 最后的话

以上测试仅是在ORACLE 11G环境完成的,不排除随着版本的升级,ORACLE优化器可能会修复上述明显的问题缺陷。

因此又在12C的场景下做了简单测试。发现只有IN子查询及UNION视图的组合,才可以顺利推入,对于其他写法及视图的组合,仍然不能谓词推入。

查看10053信息:
ST: Query in kkqstardrv:******* UNPARSED QUERY IS *******

image.png

可以看到12C环境也仅是把目标SQL转换成内连接的写法,才顺利的实现了谓词推入。

image.png

这里也可以看出,优化器找到了可以谓词推入的条件,从而顺利的推入。

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

评论