查询优化器对子查询一般采用嵌套执行的方式,即父查询中的每一行,都要执行一次子查询,这样子查询会执行很多次,效率非常低。

例如 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 子查询非嵌套的更多相关文章

  1. PLSQL Developer连接远程Oracle方法(非安装客户端)

    Oracle比较麻烦,通常需要安装oracle的客户端才能实现.通过instantclient可以比较简单的连接远程的Oracle. 1.新建目录D:\Oracle_Cleint用于存放相关文件,新建 ...

  2. PLSQL Developer连接远程Oracle方法(非安装client)

    远程连接Oracle比較麻烦,通常须要安装oracle的客户端才干实现. 通过instantclient能够比較简单的连接远程的Oracle. 1.新建文件夹D:\Oracle_Cleint用于存放相 ...

  3. 一道Oracle子查询小练习

    一道Oracle子查询小练习   昨天晚上躺在床上看Oracle(最近在学习这个),室友说出个题目让我试试.题目如下: 有如下表结构,请选择出成绩为前三名的人的信息(如果成绩相同,则算并列),表名为t ...

  4. Oracle子查询相关内容(包含TOP-N查询和分页查询)

    本节介绍Oracle子查询的相关内容: 实例用到的数据为oracle中scott用户下的emp员工表,dept部门表,数据如下: 一.子查询 1.概念:嵌入在一个查询中的另一个查询语句,也就是说一个查 ...

  5. oracle 子查询和组合函数

    oracle 子查询和组合函数 --查询与"SCOTT"在同一个部门的员工 select empno,ename,deptno from emp where deptno in ( ...

  6. Oracle子查询之简单子查询

    Oracle 简单子查询 顾名思义,简单子查询是嵌套在 SQL 语句中的另一个SELECT 语句,并且子查询只返回一列数据 1,单行子查询: 子查询 (内查询) 在主查询之前一次执行完成.子查询的结果 ...

  7. Oracle 子查询(复杂select语句)

    在执行数据操作时,如果某个操作需要依赖于另外一个 select语句的查询结果,那么就可以把 select 语句迁入到该操作语句中,这样就形成了一个子查询.实际应用中,表与表之间相互关联,相互依存,这样 ...

  8. oracle 子查询详解 in和exists的区别

    sql允许多层嵌套,子查询是嵌套在其他查询中的查询.我们可以把子查询当做一张表来看到,即外层语句可以把内嵌的查询结果当做一张表使用. 子查询查询结果有三种情况 不返回查询记录.若子查询不返回记录则主查 ...

  9. ylb:子查询(嵌套子查询)和子查询(相关子查询)

    ylbtech-SQL Server:SQL Server-子查询(嵌套子查询)和子查询(相关子查询) SQL Server 子查询(嵌套子查询)和子查询(相关子查询). 1,ylb:1,子查询(嵌套 ...

  10. SQL子连接案例

    子查询 何时使用子查询 1. 子查询作为数据源 2. 数据加工 需求:根据不同顾客的所有的账户余额划分区间,进行分组 sql语句实现如下: select 'Small Fry' name , 0 lo ...

随机推荐

  1. SEO相关配置 HTML meta标签总结与属性使用介绍

    HTML meta标签总结与属性使用介绍 <!-- 声明文档使用的字符编码 --> <meta charset='utf-8'> <!-- 优先使用 IE 最新版本和 C ...

  2. Linux-用户管理命令(必须是超级管理员-root)

    useradd   [名字] 创建一个新用户  (home  下创建) useradd -d [路径][名字]   路径中的名字是文件  , 登录用的后面的名字 passwd  [用户名] 设置密码, ...

  3. DC-SDK 开发文档

    https://resource.dvgis.cn/dc-docs/v2.x/zh/introduction/#%E5%BA%94%E7%94%A8%E9%85%8D%E7%BD%AE

  4. Spring面试攻略:如何展现你对Spring的深入理解

    什么是Spring?谈谈你对IOC和AOP的理解. Spring是一种Java开发框架,旨在简化企业级应用程序的开发和部署.它具有以下优点: 对象托管:Spring能够管理和赋值所有对象,使开发人员不 ...

  5. WPF实现Element UI风格的日期时间选择器

    背景 业务开发过程中遇到一个日期范围选择的需求,和Element UI的DateTimePicker组件比较类似,由两个日历控件组成,联动选择起始时间和结束时间. 问题 WPF中提供了一个DatePi ...

  6. Java将MySQL建表语句转换为SQLite的建表语句

    Java将MySQL建表语句转换为SQLite的建表语句 源代码: package com.fxsen.platform.core.util; import java.util.HashMap; im ...

  7. 三维模型OSGB格式轻量化压缩必要性分析

    三维模型OSGB格式轻量化压缩必要性分析 三维模型是计算机图形学和视觉效果等领域的重要应用之一.然而,由于三维模型通常包含大量的几何信息.纹理信息和其他元素,导致其占用的存储空间和计算资源非常巨大.为 ...

  8. TDD、BDD、ATDD都是什么、有什么区别?(上)

    软件开发是一个迭代过程,包括编写.测试和改进代码,直到满足需求.测试驱动开发(TDD).行为驱动开发(BDD)和验收测试驱动开发(ATDD)是支持该过程的三种方法.TDD.BDD和ATDD都是软件开发 ...

  9. 最接地气的.NET微服务框架

    前言: "人必有所执,方能有所成",从2018年底我就开始规划要写一个.NET微服务框架,5年了,今天终于正式发布了. 正文: Wing 致力于打造一个功能强大.最接地气的.NET ...

  10. 提取protobuf定义文件结构

    先安装protobuf的js支持包 npm install protobufjs test.proto文件如下所示 syntax = "proto3"; package Test; ...