前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >12C 新特性 | 标量子查询自动转换

12C 新特性 | 标量子查询自动转换

作者头像
数据和云01
发布2019-05-26 20:51:53
8990
发布2019-05-26 20:51:53
举报
文章被收录于专栏:数据库新发现数据库新发现

优化器是 Oracle 数据库最引人入胜的部件之一,因为它对每一个 SQL 语句的处理都必不可少。优化器为每个 SQL 语句确定最有效的执行计划,这是基于给定的查询的结构,可用的关于底层对象的统计信息,以及所有与优化器和执行相关的特性。因此 Oracle 在每一个版本中,优化器都引入了新特性,本文将详细讲解 12C 中标量子查询自动转换的新特性的原理,优势,适用场景和案例分享。

1、12C 标量子查询自动转换说明

首先我们来看官方文档的说明:

标量子查询是出现在 SQL 语句的 SELECT 子句的子查询。因为标量子查询不能被展开,所以一个相关的标量子查询(它引用了子查询之外的列)必须为外层查询产生的每一行被取值。考虑下面的查询:

SELECT c.cust_id, c.cust_last_name,c.cust_city, (SELECT avg(s.quantity_sold) FROM sales s WHERE s.cust_id = c.cust_id) avg_quan FROM customers c WHERE c.cust_credit_limit > 50000;

在 Oracle 11g 数据库中,对于 CUSTOMERS 表中 CUST_CREDIT_LIMIT 大于 50000 的每一行,在SALES 表上的标量子查询都必须被执行。SALES 表是大表,把它扫描多次是非常耗费资源的。

IMG_5716.png
IMG_5716.png

(图: Oracle 11g 数据库的计划显示,对于 customers 表返回的每一行,标量子查询都必须被取值)

将标量子查询展开并且将其转换为一个连接,就免除了为外层查询的每一行都进行求值的必要性。在 Oracle 12c 数据库中,标量子查询能够被展开,在这个例子中,SALES 表上的标量子查询被转换成一个 group-by 视图。group-by 视图确定会返回每组一行,正如标量子查询一样。查询中同样加入了一个外连接,这是为了确保即使当视图的结果为空时,CUSTOMERS 的数据仍然会被返回。转换后的查询如下:

SELECT c.cust_id, c.cust_last_name,c.cust_city, v.avg_quan FROM customers c, (SELECT avg(s.quantity_sold) avg_quan, s.cust_id FROM sales s GROUP BY s.cust_id) v WHERE c.cust_credit_limit > 50000 AND c.cust_id = v.cust_id(+);

IMG_5717.png
IMG_5717.png

(图: Oracle 12c 数据库的计划显示标量子查询已经被展开成外连接和 GROUP BY 视图)

2、标量子查询自动转换的优势

首先我们建立测试环境:Tab0 是小表,tab1 是大表。

IMG_5718.png
IMG_5718.png
IMG_5719.png
IMG_5719.png

---收集统计信息

IMG_5720.png
IMG_5720.png

① 在 12C 版本中

set linesize 150 alter session set statistics_level=all; select distinct b.object_type, (select max(object_id) from tab1 a where a.object_type= b.object_type) max_id from tab0 b;

执行计划信息:

IMG_5721.png
IMG_5721.png

从上面可知,标量子查询转换成了 hash join outer,性能分析可知表 tab1 只扫描了一次,整个 SQL 消耗的逻辑读为 555。

② 在 11g 版本中

alter session set optimizer_features_enable='11.2.0.4'; select distinct b.object_type, (select max(object_id) from tab1 a where a.object_type= b.object_type) max_id from tab0 b; select *from table(dbms_xplan.display_cursor(null,null,'allstats last'));

可以发现 11g 查询标量子查询成本还是蛮高的,要全表扫描 TAB1 两次,而 TAB1 刚好是大表,导致的逻辑读也刚好是 12C 中的差不多两倍,可见性能肯定相差很多。

③ 我们用 10053 分析在 12C 版本中,Oracle 到底是怎么自动改写转换

alter session set optimizer_features_enable='12.1.0.2.1'; oradebug setmypid oradebug unlimit oradebug event 10053 trace name context forever, level 12; select distinct b.object_type, (select max(object_id) from tab1 a where a.object_type= b.object_type) max_id from tab0 b; oradebug event 10053 trace name context off; oradebug tracefile_name

CVM: Merging complex viewSEL$683B0107 (#2) into SEL$C772B8D1 (#1). qbcp:******* UNPARSED QUERY IS ******* SELECT DISTINCT"B"."OBJECT_TYPE""OBJECT_TYPE","VW_SSQ_1"."MAX(OBJECT_ID)" "MAX_ID"FROM (SELECTMAX("A"."OBJECT_ID")"MAX(OBJECT_ID)","A"."OBJECT_TYPE""ITEM_1" FROM "SYS"."TAB1" "A" GROUP BY"A"."OBJECT_TYPE")"VW_SSQ_1","SYS"."TAB0" "B" WHERE"VW_SSQ_1"."ITEM_1"(+)="B"."OBJECT_TYPE" vqbcp:******* UNPARSED QUERY IS ******* SELECT MAX("A"."OBJECT_ID")"MAX(OBJECT_ID)","A"."OBJECT_TYPE""ITEM_1" FROM "SYS"."TAB1" "A" GROUP BY"A"."OBJECT_TYPE" CVM: result SEL$C772B8D1 (#1) ******* UNPARSED QUERY IS ******* SELECT DISTINCT "B"."OBJECT_TYPE""OBJECT_TYPE",MAX("A"."OBJECT_ID") "MAX_ID"FROM "SYS"."TAB1""A","SYS"."TAB0" "B" WHERE"A"."OBJECT_TYPE"(+)="B"."OBJECT_TYPE"GROUP BY "A"."OBJECT_TYPE","B".ROWID,"B"."OBJECT_TYPE" OJE: Begin: find best directive for query block SEL$841DDE77 (#1) OJE: Considering outer-join elimination on queryblock SEL$841DDE77 (#1)

从 10053 中我们也可以看出 SQL 被改写成了:

SELECT DISTINCT "B"."OBJECT_TYPE""OBJECT_TYPE","VW_SSQ_1"."MAX(OBJECT_ID)""MAX_ID" FROM (SELECTMAX("A"."OBJECT_ID") "MAX(OBJECT_ID)","A"."OBJECT_TYPE""ITEM_1" FROM "SYS"."TAB1" "A" GROUP BY"A"."OBJECT_TYPE")"VW_SSQ_1","SYS"."TAB0" "B" WHERE"VW_SSQ_1"."ITEM_1"(+)="B"."OBJECT_TYPE"

首先把标量子查询改写成了一个视图 VW_SSQ_1,然后再跟 TAB0 进行外连接,我们知道在 11G 中 tab0 有多少记录,子查询(也就是视图 VW_SSQ_1)就要执行多少次,改成连接后,先过滤符合和 tab0 条件的,这样就大大减小了对 tab0 的扫描次数。

3、标量子查询自动转换适用场景

从上述测试的结果来看,在 12C 版本中,标量子查询因为优化器会自动改写所以性能将大大提高,也省去了我们在 11G 对标量子查询的优化中手工改成外连接。那是不是在 12C 中所有标量子查询都会自动改写转换呢?下面我们来测试几个场景看看。

场景1

如上章节,聚合函数 MAX,我们知道优化器自动改写了 SQL,性能也大大提高了。

场景2

聚合函数 count

set linesize 150 alter session set statistics_level=all; select distinct b.object_type, (select count(1) from tab1 a where a.object_type =b.object_type) max_id from tab0 b; select *from table(dbms_xplan.display_cursor(null,null,'allstats last'));

执行计划信息:

由此可见 count(1)并没有发生自动改写转换,我们用 10053 事件分析为什么没有发生自动改写:

kkqctdrvTD-start on query block SEL$1 (#0) kkqctdrvTD-start: : call(in-use=2032, alloc=16344),compile(in-use=112416, alloc=115544), execution(in-use=3432, alloc=4032) SU: bypassed: Scalar subquery has null-mutatingselect item. kkqctdrvTD-cleanup: transform(in-use=0, alloc=0) : call(in-use=2032, alloc=16344),compile(in-use=113088, alloc=115544), execution(in-use=3432, alloc=4032) kkqctdrvTD-end: call(in-use=2032, alloc=16344),compile(in-use=113288, alloc=115544), execution(in-use=3432, alloc=4032) SJC: Considering set-join conversion in query blockSEL$1 (#1)

从 trace 文件中,我们可以看出 Oracle 无法转换的原因是有空值,优化器在此步不能转换。

场景3

没有聚合函数

set linesize 150 alter session set statistics_level=all; select b.object_id,(select a.object_name from tab1 a where a.object_id = b.object_id)from tab0 b; select *from table(dbms_xplan.display_cursor(null,null,'allstats last'));

执行计划信息:

IMG_5724.png
IMG_5724.png

由此可见,如果不是聚合函数在 12C 优化器也不会自动改写转换。

4、12C 标量子查询案例

下面是来自某银行系统的真实案例模拟,数据库 SQL 代码是从 11g 中直接拿来在 12C 中使用,运行出现报错。运行环境是在 IBM 小机上的,数据库版本是12.1.0.2。

create table tab4 (ck date); create table tab3(lob varchar2(10) not null, uk number not null); create table tab2 (pk_claim number not null, crt_data date);

IMG_5725.png
IMG_5725.png

Fri Feb23 10:11:57 2018 Exception [type: SIGSEGV, Address not mapped to object] [ADDR:0x0] [PC:0x3B682D6,qcsogolz()+70] [flags: 0x0, count: 1] Errors infile /u01/app/oracle/diag/rdbms/zxdb/zxdb/trace/zxdb_ora_3964.trc (incident=91434): ORA-07445: exception encountered: core dump [qcsogolz()+70][SIGSEGV] [ADDR:0x0] [PC:0x3B682D6] [Address not mapped to object] [] Incident details in: /u01/app/oracle/diag/rdbms/zxdb/zxdb/incident/incdir_91434/zxdb_ora_3964_i91434.trc Use ADRCIor Support Workbench to package the incident. See Note411.1 at My Oracle Support for error and packaging details. Fri Feb23 10:12:06 2018 Dumping diagnostic data in directory=[cdmp_20180223101206], requested by (instance=1,osid=3964), summary=[incident=91434]. Fri Feb23 10:12:11 2018 Sweep [inc][91434]: completed Sweep [inc2][91434]: completed

当我们做 insert 时,插入失败,同时会话自动断开,在 alert 日志信息出现 ORA-07445 错误,尝试在 session 级别关闭标量子查询的自动转换功能,alter session set "_optimizer_unnest_scalar_sq"=false;

IMG_5726.png
IMG_5726.png

可以看到并没有报错,可以插入成功。

当然我们也可以用 no_unnesthint 强制关闭标量子查询的展开。

IMG_5727.png
IMG_5727.png

由上案例我们可知道,在 12C 中,标量子查询自动改写的功能是有隐含参数_optimizer_unnest_all_subqueries 控制,默认是 true,意味着开启,如果遇到 bug,出错或者在自动改写转换后出现性能问题时,可以先尝试更改为 false,或者用hint(no_unnest) 避免子查询展开。

5、总结

① 12C 标量子查询优化器可以实现自动改成一个外连接,仅仅出现在一些聚集函数;

② 并不是所有的聚集函数都会出现,比如 count;

③ 如果连接列中中出现一些空值,优化器是不会自动改写转换的。因为结果不等价;

④ 子查询中没有聚集函数(AVG, MAX, MIN, STDDEV, VARIANCE, SUM, COUNT, MEDIAN)优化器是不会自动改写转换的;

⑤ 12C 标量子查询优化器自动转换受_optimizer_unnest_scalar_sq 参数的控制;

⑥ 如果在 12C 中,标量子查询优化器自动转换导致了 SQL 语句遇到 bug,出错或者再出现性能问题,可以用 alter session set "_optimizer_unnest_scalar_sq"=false 将其关闭此功能,或者用 hint no_unnest 来避免子查询展开。

资源下载

关注公众号:数据和云(OraNews)回复关键字获取

‘2017DTC’,2017 DTC 大会 PPT

‘DBALIFE’,“DBA 的一天”海报

‘DBA04’,DBA 手记4经典篇章电子书

‘RACV1’, RAC 系列课程视频及ppt

‘122ARCH’,Oracle 12.2 体系结构图

‘2017OOW’,Oracle OpenWorld 资料

‘PRELECTION’,大讲堂讲师课程资料

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年03月12日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档