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 ...
随机推荐
- SEO相关配置 HTML meta标签总结与属性使用介绍
HTML meta标签总结与属性使用介绍 <!-- 声明文档使用的字符编码 --> <meta charset='utf-8'> <!-- 优先使用 IE 最新版本和 C ...
- Linux-用户管理命令(必须是超级管理员-root)
useradd [名字] 创建一个新用户 (home 下创建) useradd -d [路径][名字] 路径中的名字是文件 , 登录用的后面的名字 passwd [用户名] 设置密码, ...
- DC-SDK 开发文档
https://resource.dvgis.cn/dc-docs/v2.x/zh/introduction/#%E5%BA%94%E7%94%A8%E9%85%8D%E7%BD%AE
- Spring面试攻略:如何展现你对Spring的深入理解
什么是Spring?谈谈你对IOC和AOP的理解. Spring是一种Java开发框架,旨在简化企业级应用程序的开发和部署.它具有以下优点: 对象托管:Spring能够管理和赋值所有对象,使开发人员不 ...
- WPF实现Element UI风格的日期时间选择器
背景 业务开发过程中遇到一个日期范围选择的需求,和Element UI的DateTimePicker组件比较类似,由两个日历控件组成,联动选择起始时间和结束时间. 问题 WPF中提供了一个DatePi ...
- Java将MySQL建表语句转换为SQLite的建表语句
Java将MySQL建表语句转换为SQLite的建表语句 源代码: package com.fxsen.platform.core.util; import java.util.HashMap; im ...
- 三维模型OSGB格式轻量化压缩必要性分析
三维模型OSGB格式轻量化压缩必要性分析 三维模型是计算机图形学和视觉效果等领域的重要应用之一.然而,由于三维模型通常包含大量的几何信息.纹理信息和其他元素,导致其占用的存储空间和计算资源非常巨大.为 ...
- TDD、BDD、ATDD都是什么、有什么区别?(上)
软件开发是一个迭代过程,包括编写.测试和改进代码,直到满足需求.测试驱动开发(TDD).行为驱动开发(BDD)和验收测试驱动开发(ATDD)是支持该过程的三种方法.TDD.BDD和ATDD都是软件开发 ...
- 最接地气的.NET微服务框架
前言: "人必有所执,方能有所成",从2018年底我就开始规划要写一个.NET微服务框架,5年了,今天终于正式发布了. 正文: Wing 致力于打造一个功能强大.最接地气的.NET ...
- 提取protobuf定义文件结构
先安装protobuf的js支持包 npm install protobufjs test.proto文件如下所示 syntax = "proto3"; package Test; ...