性能优化之查询转换 - 子查询类

子查询,是SQL中常见的一种写法。对于优化器来说,子查询是较难优化的部分。Oracle提供了多种方式,对子查询进行查询转换。

一、子查询推进

子查询推进(又称子查询推入)是指优化器将子查询提前进行评估,使得优化器可以更早地介入优化以获得更优质的执行计划。这个技术可以通过提示PUSH_SUBQ/NO_PUSH_SUBQ控制。下面通过一个示例看看结果。

SQL> create table t_users as select * from dba_users;
//表已创建
SQL> create index idx_user_created on t_users(created);
//索引已创建
SQL> create table t_objects as select * from dba_objects;
//表已创建

执行以下语句:

SQL> select /*+ no_push_subq(@inv)*/ /*hf1*/ *
from t_objects u
where created >
(select /*+ qb_name(inv)*/ max(created)
from t_users );

执行计划如下:

注:在这个语句中,我们通过提示强制不使用子查询推进技术。由执行计划可见,执行是按照T_OBJECTS和T_USRES进行的一个索引的嵌套循环。

使用子查询推进:

SQL> select /*hf2*/ *
from t_objects u
where created >
( select /*+ qb_name(inv)*/ max(created)
 from t_users );

执行计划如下:

在这个示例中,Oracle使用了子查询推入技术,且可以在OutLine中看到PUSH_SUBQ字样。从执行计划可见,没有出现两表关联,提前处理了子查询,生成MAX CREATED,然后全表扫描T_OBJECTS进行条件过滤,显然这种方式效率更高。

二、子查询嵌套、展开

子查询解嵌套是指优化器将子查询展开,和外部的查询进行关联、合并,从而得到更优的执行计划。可以通过UNNEST/NO_UNNEST提示控制是否进行解嵌套。采用这种技术通常可以提高执行效率,原因是如果不解嵌套,子查询往往是最后执行的,作为FILTER条件来过滤外部查询;而一旦展开,优化器就可以选择表关联等更高效的执行方式,以提高效率。下面通过几个示例说明各种解嵌套的形式。

先看第一个示例:

SQL> create table t_tables as select * from dba_tables;
Table created.
SQL> select * from t_objects o where exists(select /*+ qb_name(inv)*/ 1 from t_tables t where t.owner=o.owner and t.table_name=o.object_name);

执行计划如下:

在这个示例中,对EXISTS的子查询进行了解嵌套,然后选择了半连接(SEMI JOIN)的关联方式。

再来看一个示例。

SQL> select * from t_objects o where not exists (select /*+ qb_name(inv)*/ 1 from t_tables t where t.owner=o.owner and t.table_name=o.object_name);

执行计划如下:

在这个示例中,对NOT EXISTS的子查询进行了解嵌套,然后选择了反连接(ANTI JOIN)的关联方式。

三、子查询分解

子查询分解是由WITH创建的复杂查询语句并存储在临时表中,可按照与一般表相同的方式使用该临时表的功能。这种方式可以把一个复杂的查询分成很多简单的部分,并让优化器去决定是产生中间数据集还是构建该查询复杂的扩展形式并对其进行优化。这种方式的优点在于,使用WITH子句的子查询在复杂查询语句中只需要执行一次,但结果可以在同一个查询语句中被多次使用。缺点在于,这种方式不允许语句变形,所以无效的情况较多。

下面看一个示例。

SQL> with user_obj as
    (select owner,count(*) cnt
        from t_objects group by owner)
select u.user_id,u.username,o.cnt
from t_users u,user_obj o
where u.username=o.owner;

子查询定义为user_obj,在执行计划中以一个视图的形式(ID=2的步骤)出现,并与T_USRES进行了哈希关联。

上述过程并没有生成临时表,可通过一个提示materialize强制优化器创建临时表。

SQL> with user_obj as
(select --+ materialize
owner,count(*) cnt
from t_objects group by owner )
select u.user_id,u.username,o.cnt
from t_users u,user_obj o
where u.username=o.owner;

执行计划如下:

引入了materialize提示后,由ID=2步骤可见,系统生成了一个临时表SYS_TEMP_XXX,并由这个表在后面与T_USERS进行了关联查询。

四、子查询合并

在语义等价的前提下,如果多个子查询产生的结果集相同,则优化器可以使用这种技术将多个子查询合并为一个子查询。这样的好处在于减少多次扫描产生的开销。可以通过NO_COALESCE_SQ/COALESCE_SQ提示来控制。下面看个示例:

select /*+ qb_name(mn)*/ t.*
from t_tables t  where exists
(select /*+ qb_name(sub1)*/ 1
from t_tablespaces ts
where t.tablespace_name=ts.tablespace_name and ts.block_size=8)
and exists
(select /*+ qb_name(sub2)*/ 1
from t_tablespaces ts
where t.tablespace_name=ts.tablespace_name);

执行计划如下:

在这个查询中,外部对T_TABLES表的查询要同时满足SUB1和SUB2两个子查询,而SUB1在语义上又是SUB2的子集,因此优化器将两个子查询进行了合并(只进行一次对T_TABLESPACES表的扫描),然后与外部表T_TABLES进行半连接。

那么如果语义不等价又会怎么样呢?

select /*+ qb_name(mn)*/ t.*
from t_tables t
where exists
(select /*+ qb_name(sub1)*/ 1
from t_tablespaces ts
where t.tablespace_name=ts.tablespace_name and ts.block_size=8)
and exists
(select /*+ qb_name(sub2)*/ 1
from t_tablespaces ts
where t.tablespace_name=ts.tablespace_name and ts.block_size=16);

执行计划如下:

在这个查询语句中,外部查询要满足两个子查询—SUB1和SUB2,但两者条件不同,不能简单合并。因此在执行计划中,分别对两者进行了扫描(直观感觉就是对T_TABLESPACES进行了两次扫描),然后再做关联查询。

五、子查询优化

子查询实体化是指在上面WITH定义的查询中,将查询结果写入一张临时表中,后续的查询直接利用临时表中的数据。可以通过MATERIALIZE提示来控制。下面看个示例。

SQL> with v as
(select /*+ MATERIALIZE */ * from t_users where username='SYS')
select count(*) from v;

执行计划如下:

在ID=2的步骤中生成了一张临时表SYS_TEMP_xxx,并且这个临时表在后面会被直接使用。如果去掉提示会怎样呢?

SQL> with v as
(select * from t_users where username='SYS')
select count(*) from v;

执行计划如下:

此时不再生成临时表,直接解嵌套执行。

-----the end

本文来自合作伙伴“DBGEEK”

时间: 2024-10-23 02:40:19

性能优化之查询转换 - 子查询类的相关文章

MySQL基本SQL查询语句:多表查询和子查询示例

一.简单查询:基本语法:  代码如下 复制代码 SELECT * FROM tb_name; 查询全部  代码如下 复制代码 SELECT field1,field2 FROM tb_name; 投影  代码如下 复制代码 SELECT [DISTINCT] * FROM tb_name WHERE qualification;  选择 说明: FROM子句: 要查询的关系         表.多个表.其它SELECT语句 WHERE子句:布尔关系表达式,主要包含如下这几类表达式: 比较:=.>

mysql的查询、子查询及连接查询

一.mysql查询的五种子句         where(条件查询).having(筛选).group by(分组).order by(排序).limit(限制结果数)           1.where常用运算符:             比较运算符                 > ,  < ,=  , != (< >),>=   ,   <=                   in(v1,v2..vn)                   between v1

解析mysql的查询、子查询及连接查询教程

一.mysql查询的五种子句         where(条件查询).having(筛选).group by(分组).order by(排序).limit(限制结果数)           1.where常用运算符:             比较运算符                 > ,  < ,=  , != (< >),>=   ,   <=                  in(v1,v2..vn)                  between v1 a

详解MySql基本查询、连接查询、子查询、正则表达查询_Mysql

查询数据指从数据库中获取所需要的数据.查询数据是数据库操作中最常用,也是最重要的操作.用户可以根据自己对数据的需求,使用不同的查询方式.通过不同的查询方式,可以获得不同的数据.MySQL中是使用SELECT语句来查询数据的.在这一章中将讲解的内容包括. 1.查询语句的基本语法 2.在单表上查询数据 3.使用聚合函数查询数据 4.多表上联合查询 5.子查询 6.合并查询结果 7.为表和字段取别名 8.使用正则表达式查询 什么是查询? 怎么查的? 数据的准备如下: create table STUD

mybatis性能优化二之多对多查询:用一次请求解决n次请求查询

<resultMap type="com.cn.vo.Teacher" id="teacher"> <id property="id" column="id" javaType="int" jdbcType="INTEGER" /> <result property="name" column="name" javaT

Oracle CBO几种基本的查询转换详解

Oracle CBO几种基本的查询转换详解 查询转换(Query Transformation),又称为逻辑优化(Logical Optimization),也称为软优化,即查询转换器在逻辑上对语句做一些语义等价转换,从而能使优化器生成效率更高的执行计划. 语句在被提交到Oracle后,解析器(Parser)会对SQL语句的语法.语义进行分析,并将查询中的视图展开.划分为小的查询块(Query Block).它们是嵌套或者相互关联的,而查询形式则决定了它们之间是如何关联的.这些查询块被传送给了查

《Oracle高性能SQL引擎剖析:SQL优化与调优机制详解》一3.1 启发式查询转换

3.1 启发式查询转换 所有的启发式查询转换都是基于一套优化器内建的规则.在查询转换阶段,转换器会逐个针对这些规则对查询进行检查,确定其是否满足转换规则,一旦满足,转换器就对其进行转换. 3.1.1 简单视图合并 我们知道,视图(View)的实质就是一条查询语句.在解析阶段,语句中的每个视图都会被展开至一个查询块中.如果未做视图合并,优化器则会单独分析每个视图,并为定义视图的查询语句生成一个视图子计划.然后再分析整个查询的其他部分,并生成执行计划.在这种情况下,由于视图的执行计划和整体执行计划不

解决SQL查询安全性及性能优化实现方法

SQL注入的原理 什么SQL注入 将SQL代码插入到应用程序的输入参数中,之后,SQL代码被传递到数据库执行.从而达到对应用程序的攻击目的. 注入原理 常见攻击方法 检测是否可以注入[检测注入点] 示例:http://192.168.0.1/temp.aspx?id=a or 1=1-- 如果上面语句可以执行说明可以实现注入,则可以   利用系统过程.系统表注入数据库 示例[给系统添加一个用户,黑客可以实现远程登录控制服务器]:http://192.168.0.1/temp.aspx?id=1;

MySQL的子查询及相关优化学习教程_Mysql

一.子查询 1.where型子查询(把内层查询结果当作外层查询的比较条件) #不用order by 来查询最新的商品 select goods_id,goods_name from goods where goods_id = (select max(goods_id) from goods); #取出每个栏目下最新的产品(goods_id唯一) select cat_id,goods_id,goods_name from goods where goods_id in(select max(g