PostgreSQL 提升子连接与 ORACLE 子查询非嵌套
查询优化器对子查询一般采用嵌套执行的方式,即父查询中的每一行,都要执行一次子查询,这样子查询会执行很多次,效率非常低。
例如 exists、not exists 逐行取出经行匹配处理,项目中使用子查询的地方非常多,如何写出高效的sql,掌握子查询的优化是非常有必要的。
一、需要了解的概念:
PostgreSQL数据库基于子查询所在的位置和作用的不同,将子查询细分成了两类,一类称为子连接(SubLink),另一类称为子查询(SubQuery)。
如何区分子连接和子查询?
通常而言,如果它是以范围表的方式存在的,那么就称为子查询。
explain
select e1.*
from emp e1,
(select * from emp where deptno = 10) e2
where e1.empno = e2.empno
Hash Join (cost=1.21..2.40 rows=3 width=41)
Hash Cond: (e1.empno = emp.empno)
-> Seq Scan on emp e1 (cost=0.00..1.14 rows=14 width=41)
-> Hash (cost=1.18..1.18 rows=3 width=5)
-> Seq Scan on emp (cost=0.00..1.18 rows=3 width=5)
Filter: (deptno = '10'::numeric)
如果它以表达式的方式存在,那么就称为子连接。
-- 子链接1
explain
select e.empno, (select avg(sal) from emp e1 where e.deptno = e1.deptno)
from emp e
Seq Scan on emp e (cost=0.00..17.94 rows=14 width=37)
SubPlan 1
-> Aggregate (cost=1.19..1.20 rows=1 width=32)
-> Seq Scan on emp e1 (cost=0.00..1.18 rows=5 width=5)
Filter: (e.deptno = deptno)
-- 子链接2
explain
select *
from emp e1
where sal in (select sal from emp where e1.deptno = 10)
Seq Scan on emp e1 (cost=0.00..9.43 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Result (cost=0.00..1.14 rows=14 width=5)
One-Time Filter: (e1.deptno = '10'::numeric)
-> Seq Scan on emp (cost=0.00..1.14 rows=14 width=5)
一般情况下,子连接的执行效率往往是比子查询低很多,因为子连接是逐行处理,会产生filter,而PostgreSQL 优化器会在某些情况下对子连接进行提升为子查询,从而对filter操作进行消除,提升查询效率。
二、in 子连接
** in 子连接提升子查询写法:**
explain select * from emp where deptno in (select deptno from dept);
Hash Join (cost=1.09..2.31 rows=14 width=41)
Hash Cond: (emp.deptno = dept.deptno)
-> Seq Scan on emp (cost=0.00..1.14 rows=14 width=41)
-> Hash (cost=1.04..1.04 rows=4 width=5)
-> Seq Scan on dept (cost=0.00..1.04 rows=4 width=5)
可以被提升,优化器相会内部重写成内连接。
explain select emp.* from emp inner join dept on emp.deptno = dept.deptno;
Hash Join (cost=1.09..2.31 rows=14 width=41)
Hash Cond: (emp.deptno = dept.deptno)
-> Seq Scan on emp (cost=0.00..1.14 rows=14 width=41)
-> Hash (cost=1.04..1.04 rows=4 width=5)
-> Seq Scan on dept (cost=0.00..1.04 rows=4 width=5)
表明此 in 子连接被优化,优化后采用hash join算法。
in 子连接无法提升子查询写法:
-- 子连接包含谓词过滤写法,无法提升子查询
explain select * from emp e1 where sal in (select sal from emp where e1.deptno = 10);
Seq Scan on emp e1 (cost=0.00..9.43 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Result (cost=0.00..1.14 rows=14 width=5)
One-Time Filter: (e1.deptno = '10'::numeric)
-> Seq Scan on emp (cost=0.00..1.14 rows=14 width=5)
-- 使用 not in 也是无法提升子查询,not in与 <> all含义相同
explain select * from emp e1 where sal not in (select sal from emp );
Seq Scan on emp e1 (cost=1.18..2.35 rows=7 width=41)
Filter: (NOT (hashed SubPlan 1))
SubPlan 1
-> Seq Scan on emp (cost=0.00..1.14 rows=14 width=5)
表明此 in 子连接未被优化,无法消除filter操作,只能逐行处理。
三、exists 子连接
** exists 子连接提升子查询写法:**
explain
select e.* from emp e where exists(select * from emp e2 where e.empno = e2.empno);
Hash Join (cost=1.32..2.50 rows=14 width=41)
Hash Cond: (e.empno = e2.empno)
-> Seq Scan on emp e (cost=0.00..1.14 rows=14 width=41)
-> Hash (cost=1.14..1.14 rows=14 width=5)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=5)
-- 当加入where e.deptno = 10 条件在子链接时,仍然支持上拉
explain
select e.*
from emp e
where exists(select * from emp e2 where e.deptno = 10)
Nested Loop Semi Join (cost=0.00..2.45 rows=3 width=41)
-> Seq Scan on emp e (cost=0.00..1.18 rows=3 width=41)
Filter: (deptno = '10'::numeric)
-> Materialize (cost=0.00..1.21 rows=14 width=0)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=0)
exists 子连接无法提升子查询写法:
explain
select e.*
from emp e
where exists(select sum(sal) from emp e2 where e.empno = e2.empno);
Seq Scan on emp e (cost=0.00..17.80 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Aggregate (cost=1.18..1.19 rows=1 width=32)
-> Seq Scan on emp e2 (cost=0.00..1.18 rows=1 width=5)
Filter: (e.empno = empno)
explain
select e.*
from emp e
where exists(select * from emp e2 where e2.deptno = 10);
Result (cost=0.39..1.53 rows=14 width=41)
One-Time Filter: $0
InitPlan 1 (returns $0)
-> Seq Scan on emp e2 (cost=0.00..1.18 rows=3 width=0)
Filter: (deptno = '10'::numeric)
-> Seq Scan on emp e (cost=0.39..1.53 rows=14 width=41)
explain
select e.* from emp e where not exists(select 1 from emp e2 )
Result (cost=0.08..1.22 rows=14 width=41)
One-Time Filter: (NOT $0)
InitPlan 1 (returns $0)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=0)
-> Seq Scan on emp e (cost=0.08..1.22 rows=14 width=41)
in和exists都存在不被优化的可能,对于in和exists的选择,当父查询结果集小于子查询结果集则选择exists,如果父查询结果集大于子查询结果集选择in。
四、所有的all子链接都不支持上拉
explain
select * from emp where sal > all (select sal from emp e2);
Seq Scan on emp (cost=0.00..9.89 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Materialize (cost=0.00..1.21 rows=14 width=5)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=5)
explain
select * from emp where sal = all (select sal from emp e2);
Seq Scan on emp (cost=0.00..9.89 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Materialize (cost=0.00..1.21 rows=14 width=5)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=5)
explain
select * from emp where sal < all (select sal from emp e2);
Seq Scan on emp (cost=0.00..9.89 rows=7 width=41)
Filter: (SubPlan 1)
SubPlan 1
-> Materialize (cost=0.00..1.21 rows=14 width=5)
-> Seq Scan on emp e2 (cost=0.00..1.14 rows=14 width=5)
关于all的查询都都是以子连接的形式,不会上拉。
some和any是等效的,这里不做演示了。
五、join与子查询固化或rewrite
join或子查询的优化,属于优化器优化JOIN的范畴。
当用户的QUERY涉及到多个JOIN对象,或者涉及到多个子查询时,优化器可以选择是否改变当前的SQL,产生更多的plan选择更优的执行计划。
postgresql.conf文件中:
#from_collapse_limit = 8
当from列表的对象少于from_collapse_limit时,优化器可以将子查询提升到上层进行JOIN,从而可能选择到更优的执行计划。
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
当使用显示的JOIN时(除了full join),例如a join b join c join d,优化器可以重排JOIN的顺序,以产生更多的PLAN选择更优的执行计划。
如果join_collapse_limit=1,则不重排,使用SQL写法提供的顺序。
如果用户要固化JOIN顺序,请使用显示的JOIN,同时将join_collapse_limit设置为1。
如果用户不打算提升子查询,同样的,将from_collapse_limit 设置为1即可。
六、等价改写
子查询中没有group by子句,也没有聚集函数,则可使用下面的等价转换
val>all(select...) to val>max(select...)
val<all(select...) to val<min(select...)
val>any(select...) to val>min(select...)
val<any(select...) to val<max(select...)
val>=all(select...) to val>=max(select...)
val<=all(select...) to val<=min(select...)
val>=any(select...) to val>=min(select...)
val<=any(select...) to val<=max(select...)
通常,聚集函数min(),max()的执行效率要比any、all效率高
七、ORACLE 的子查询非嵌套
子查询非嵌套(subquery Unnesting),查询转换技巧中强烈要求掌握的技能。
** 当where子查询中有in、not in、exits、not exists等,CBO会尝试将子查询展开(unnest),从而消除FILTER。这个过程叫作子查询非嵌套。子查询非嵌套的目的就是消除FILTER。**
当子查询语句含有exists或not exists时,子查询中有固化子查询关键词(union/union all / start connect by/rownum /cube / rollup),那么执行计划就很容易产生FILTER。
八、结束语
1、postgresql子查询的优化思路,子查询不用执行多次
2、优化器可以根据统计信息来选择不同的连接方法和不同的连接顺序
3、子查询中的连接条件,过滤条件分别变成了父查询的连接条件、过滤条件、优化器可以对这些条件进行下推、提高执行效率
4、将子查询优化为表连接后,子查询只需要执行一次、而优化器可以根据统计信息来选择不同的连接方式和连接顺序、子查询的连接条件和过滤条件分别变成父查询的条件。
5、这些查询中all是完全不支持上拉子子链接的,in和exists存在不被优化的可能。
6、not exists虽然没有被上拉,但是被优化为只执行一次,相对于not in稍好。
7、可使用等价改写的方式优化8.可根据配置文件,固化子查询,以及表的连接顺序。
8、无论是PostgreSQL中的子连接提升查询还是ORACLE中的子查询非嵌套主要的目的就是优化子查询,消除filter。
PostgreSQL 提升子连接与 ORACLE 子查询非嵌套的更多相关文章
- PLSQL Developer连接远程Oracle方法(非安装客户端)
Oracle比较麻烦,通常需要安装oracle的客户端才能实现.通过instantclient可以比较简单的连接远程的Oracle. 1.新建目录D:\Oracle_Cleint用于存放相关文件,新建 ...
- PLSQL Developer连接远程Oracle方法(非安装client)
远程连接Oracle比較麻烦,通常须要安装oracle的客户端才干实现. 通过instantclient能够比較简单的连接远程的Oracle. 1.新建文件夹D:\Oracle_Cleint用于存放相 ...
- 一道Oracle子查询小练习
一道Oracle子查询小练习 昨天晚上躺在床上看Oracle(最近在学习这个),室友说出个题目让我试试.题目如下: 有如下表结构,请选择出成绩为前三名的人的信息(如果成绩相同,则算并列),表名为t ...
- Oracle子查询相关内容(包含TOP-N查询和分页查询)
本节介绍Oracle子查询的相关内容: 实例用到的数据为oracle中scott用户下的emp员工表,dept部门表,数据如下: 一.子查询 1.概念:嵌入在一个查询中的另一个查询语句,也就是说一个查 ...
- oracle 子查询和组合函数
oracle 子查询和组合函数 --查询与"SCOTT"在同一个部门的员工 select empno,ename,deptno from emp where deptno in ( ...
- Oracle子查询之简单子查询
Oracle 简单子查询 顾名思义,简单子查询是嵌套在 SQL 语句中的另一个SELECT 语句,并且子查询只返回一列数据 1,单行子查询: 子查询 (内查询) 在主查询之前一次执行完成.子查询的结果 ...
- Oracle 子查询(复杂select语句)
在执行数据操作时,如果某个操作需要依赖于另外一个 select语句的查询结果,那么就可以把 select 语句迁入到该操作语句中,这样就形成了一个子查询.实际应用中,表与表之间相互关联,相互依存,这样 ...
- oracle 子查询详解 in和exists的区别
sql允许多层嵌套,子查询是嵌套在其他查询中的查询.我们可以把子查询当做一张表来看到,即外层语句可以把内嵌的查询结果当做一张表使用. 子查询查询结果有三种情况 不返回查询记录.若子查询不返回记录则主查 ...
- ylb:子查询(嵌套子查询)和子查询(相关子查询)
ylbtech-SQL Server:SQL Server-子查询(嵌套子查询)和子查询(相关子查询) SQL Server 子查询(嵌套子查询)和子查询(相关子查询). 1,ylb:1,子查询(嵌套 ...
- SQL子连接案例
子查询 何时使用子查询 1. 子查询作为数据源 2. 数据加工 需求:根据不同顾客的所有的账户余额划分区间,进行分组 sql语句实现如下: select 'Small Fry' name , 0 lo ...
随机推荐
- 开源资产管理系统chemex
目录 项目地址 部署 初始化环境 安装docker.Mariadb 配置数据库 docker 脚本 运行 项目地址 gitee项目链接 部署 参考链接:Centos7使用Docker部署Chemex资 ...
- Mysql高级8-触发器
一.触发器 触发器是与表有关的数据库对象,指在insert/update/delete之前或者之后,触发并执行触发器中定义的sql语句集合,触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志 ...
- Django模板(请用Django2.0版本完成)
1. 在learn目录下新建一个templates文件夹,里面新建一个home.html (1) 很简单的,就直接右键learn,新建文件夹,完成后,继续右键templates,创建文档,后缀名为ht ...
- 《代码整洁之道 Clean Code》学习笔记 Part 1
前段时间在看<架构整洁之道>,里面提到了:构建一个好的软件系统,应该从写整洁代码做起.毕竟,如果建筑使用的砖头质量不佳,再好的架构也无法造就高质量的建筑.趁热打铁,翻出<代码整洁之道 ...
- Vue源码学习(三):<templete>渲染第二步,创建ast语法树
好家伙,书接上回 在上一篇Vue源码学习(二):<templete>渲染第一步,模板解析中,我们完成了模板解析 现在我们继续,将模板解析的转换为ast语法树 1.前情提要 代码已开 ...
- 用OLED屏幕播放视频(1): 项目介绍
下面的系列文章记录了如何使用一块linux开发扳和一块OLED屏幕实现视频的播放: 项目介绍 为OLED屏幕开发I2C驱动 使用cuda编程加速视频处理 这篇文章主要对项目的实现做整体的介绍, 包括硬 ...
- KRPANO开发拍摄拼图视频常用软件分享
KRPano开发拍摄拼图视频常用软件分享,包含了KRPano开发,拼图,视频等软件. 包括如下软件: 全景照片拼接,全景照片查看,全景视频拼接,全景视频查看,全景视频插件,全景漫游制作,KRPano资 ...
- 图解 LeetCode 算法汇总——二分查找
二分查找(Binary Search)是一种在有序数组中查找特定元素的高效算法.它的基本思想是将目标值与数组中间的元素进行比较,如果目标值小于中间元素,则在数组的左半部分继续查找,否则在右半部分查找, ...
- 在C#中如何自定义配置上周和本周起始日来查询业务数据?
作者:西瓜程序猿 主页传送门:https://www.cnblogs.com/kimiliucn 前言 在做某个报表管理功能时,有一个需求:需要根据自定义配置的[周起始日]来统计上周.本周的订单数据. ...
- Note -「Suffix Automaton」SAM
Part. 1 基本信息 Part. 1-1 SAM 的构成. SAM 由两个东西构成,一个是一个 DAWG,还有一棵外向树,叫 parent tree. 比如,给你一个字符串 \(S=\sf abb ...