关于查询转换的一些简单分析(一)

简介: 在sql解析器中,在生成执行计划的时候,会在多个执行计划中选择最优的计划,在这个过程中,查询转换就是一个很重要的过程。 虽然最终的执行结果没有变化,但是从优化器的角度来看,查询转换的结果会更好。

在sql解析器中,在生成执行计划的时候,会在多个执行计划中选择最优的计划,在这个过程中,查询转换就是一个很重要的过程。
虽然最终的执行结果没有变化,但是从优化器的角度来看,查询转换的结果会更好。
大体来说查询转换有以下几种类型。
视图合并
子查询解嵌套
谓词推进
物化视图查询重写

比如
select *from emp where deptno in (select deptno from dept)
会在查询转换中转换为下面的样子.
select e.* from emp e,dept d where e.deptno=d.deptno;
从查询转换的角度来说,两者的查询效果是基本一致的。

--视图合并
select *
from emp e,
     (select deptno from emp) s_view
where e.deptno=s_view.deptno;

70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3638257876
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    71 |  2911 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN         |      |    71 |  2911 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| EMP  |    14 |   532 |     2   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |    42 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="DEPTNO")


来比较一下是否视图合并的结果。
select
*
from emp e,
     (select /*+no_merge*/ deptno from emp) s_view
where e.deptno=s_view.deptno

70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2243607326
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    71 |  3621 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN          |      |    71 |  3621 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL | EMP  |    14 |   532 |     2   (0)| 00:00:01 |
|   3 |   VIEW              |      |    14 |   182 |     2   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| EMP  |    14 |    42 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="S_VIEW"."DEPTNO")


可以比较上面两个查询的时候,会发现第二个执行计划的VIEW关键字表示,执行计划是保持原样的,会单独处理视图。
使用圆括号把子查询括起来不一定在sql执行的时候会保证子查询块会单独执行。
这个时候可以根据要求来选择是否选择视图合并。

视图合并在大体来说性能还是不错的,不过在使用order by, 聚合函数,分析函数,group等等的时候,视图合并的功能还是会受到限制或者无法启用。
而且在试图合并的功能受到限制的时候,如果硬要启用视图合并功能,可能查询的结果会有一些变化,这个时候需要你来判断是否确定要启用视图合并。
select e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;

12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |    14 |   546 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |      |    14 |   546 |     6  (34)| 00:00:01 |
|   2 |   VIEW               |      |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |      |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
|*  5 |   TABLE ACCESS FULL  | EMP  |    13 |   169 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="V"."DEPTNO")
   5 - filter("E"."SAL">1000)

 


select /*+ merge(v)*/ e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;

12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 4115741206
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    68 |  2176 |     6  (34)| 00:00:01 |
|   1 |  HASH GROUP BY      |      |    68 |  2176 |     6  (34)| 00:00:01 |
|*  2 |   HASH JOIN         |      |    68 |  2176 |     5  (20)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL| EMP  |    13 |   325 |     2   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("E"."DEPTNO"="DEPTNO")
   3 - filter("E"."SAL">1000)

 

--子查询解嵌套
子查询嵌套和视图合并的不同之处在于视图合并是在select xxx from的部分,而子查询嵌套式在where xxxx的部分。

select *from emp where deptno in (select deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    14 |   532 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("DEPTNO" IS NOT NULL)

         
先来比较一下是否启用子查询嵌套的区别 ,如果不需要启用子查询嵌套,可以使用Hint no_unnest来引导。
select *from emp where deptno in (select /*+ no_unnest*/deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1783302997
------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |     5 |   190 |     2   (0)| 00:00:01 |
|*  1 |  FILTER            |         |       |       |            |          |
|   2 |   TABLE ACCESS FULL| EMP     |    14 |   532 |     2   (0)| 00:00:01 |
|*  3 |   INDEX UNIQUE SCAN| PK_DEPT |     1 |     3 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "DEPT" "DEPT"
              WHERE "DEPTNO"=:B1))
   3 - access("DEPTNO"=:B1)

可以从谓语部分看出来,两个的执行计划还是有很大的不同,第二个执行计划是未启用子查询解嵌套的形式。会在子查询中走索引,然后通过exists子句来和外部查询的deptno字段连接。
而第一个执行计划直接走了一个全表扫描,对于deptno的部分直接使用条件deptno is not null来过滤了。


select *from emp outer
where outer.sal>(select avg(inner.sal) from emp inner where inner.deptno=outer.deptno)
6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1245077725
--------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |         |     1 |    64 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |         |     1 |    64 |     6  (34)| 00:00:01 |
|   2 |   VIEW               | VW_SQ_1 |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |         |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP     |    14 |    98 |     2   (0)| 00:00:01 |
|   5 |   TABLE ACCESS FULL  | EMP     |    14 |   532 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ITEM_1"="OUTER"."DEPTNO")
       filter("OUTER"."SAL">"AVG(INNER.SAL)")

这个例子中子查询转换为了一个内嵌式图,然后与外部的查询合并连接,相关列成为了连接条件,子查询的剩余部分成为了内嵌视图。

sql的执行方式和下面的sql类似。
select *from emp outer,
(select inner.deptno,avg(inner.sal) avg_sal from emp inner group by inner.deptno)inner where inner.deptno=outer.deptno
and outer.sal>inner.avg_sal;

6 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     1 |    64 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |      |     1 |    64 |     6  (34)| 00:00:01 |
|   2 |   VIEW               |      |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |      |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
|   5 |   TABLE ACCESS FULL  | EMP  |    14 |   532 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("INNER"."DEPTNO"="OUTER"."DEPTNO")
       filter("OUTER"."SAL">"INNER"."AVG_SAL")

关于谓词推进和物化视图的部分,稍后补充。

目录
相关文章
|
安全 Windows
WindowsXP现在还能使用吗
WindowsXP现在还能使用吗
622 2
|
Java Nacos
SpringBoot 整合 Nacos
上一篇笔者已经介绍怎么安装 Nacos 了,这篇就开始重头戏。 搭建SpringBoot项目。
2688 1
|
机器学习/深度学习 人工智能 编解码
阿里云AI技术:让老照片焕发新生,让你仿佛穿越过去
本文分享了使用阿里云的PAI-DSW机器学习平台对老照片进行修复和上色的体验心得。特别强调了上色算法和图像超分算法的应用,这两个算法使得原本的黑白、模糊的老照片获得了生动的色彩和清晰的细节。通过SDWebUI的直观操作界面,整个修复和上色过程变得既简单又高效,为珍藏历史和回忆的人们提供了一个宝贵的工具。
阿里云AI技术:让老照片焕发新生,让你仿佛穿越过去
|
编解码
按照以下步骤对老照片进行修复
按照以下步骤对老照片进行修复
589 1
|
存储 Unix API
FreeNAS
FreeNAS是一款开源的网络附加存储(NAS)操作系统,它基于FreeBSD操作系统,提供了强大的存储管理和数据保护功能。FreeNAS支持多种文件共享协议,包括SMB/CIFS、NFS、AFP、FTP、iSCSI等,使得用户可以方便地在不同的操作系统和设备之间共享文件和数据。
754 0
阿里云COM域名续费优惠口令“商标优选即买即用”
2023阿里云COM域名续费优惠口令“商标优选即买即用”,阿里云COM域名注册或续费均可使用优惠口令,如果是新用户还可以领取com域名注册代金券,企业新用户注册com域名优惠价1元首年,个人新用户注册com域名首年33元一年,新手站长分享阿里云com域名注册价格、续费价格、注册优惠口令、续费优惠口令和注册代金券领取:
569 0
阿里云COM域名续费优惠口令“商标优选即买即用”
|
前端开发 JavaScript
一个基于Vue3和ElementPlus的后台管理布局
一个基于Vue3和ElementPlus的后台管理布局
488 0
|
负载均衡 安全 前端开发
亿级流量架构网关设计思路,常用网关对比,写得太好了。。(1)
本文准备围绕七个点来讲网关,分别是网关的基本概念、网关设计思路、网关设计重点、流量网关、业务网关、常见网关对比,对基础概念熟悉的朋友可以根据目录查看自己感兴趣的部分。
455 0
亿级流量架构网关设计思路,常用网关对比,写得太好了。。(1)
|
机器学习/深度学习 算法 机器人
机器学习超参调优:常用八种方法
超参数调优是机器学习例程中的基本步骤之一。该方法也称为超参数优化,需要搜索超参数的最佳配置以实现最佳性能。
1384 0
机器学习超参调优:常用八种方法

热门文章

最新文章