Oracle SQL优化③——表的连接方式

news2024/11/25 23:11:00

前言

表(结果集)与表(结果集)之间的连接方式非常重要,如果CBO选择了错误的连接方式,本来几秒就能出结果的SQL可能执行一天都执行不完。如果想要快速定位超大型SQL性能问题,就必须深入理解表连接方式。在多表关联的时候,一般情况下只能是两个表先关联,两表关联之后的结果再和其他表/结果集关联,如果执行计划中出现了Filter,这时可以一次性关联多个表。

一.嵌套循环(NESTED LOOPS)

嵌套循环的算法:驱动表返回一行数据,通过连接列传值给被驱动表,驱动表返回多少行,被驱动表就要被扫描多少次。
嵌套循环可以快速返回两表关联的前几条数据,如果SQL中添加了HINT:FIRST_ROWS,在两表关联的时候,优化器更倾向于嵌套循环。
嵌套循环驱动表应该返回少量数据。如果驱动表返回了100万行,那么被驱动表就会被扫描100万次。这个时候SQL会执行很久,被驱动表会被误认为热点表,被驱动表连接列的索引也会被误认为热点索引。
嵌套循环被驱动表必须走索引。如果嵌套循环被驱动表的连接列没包含在索引中,那么被驱动表就只能走全表扫描,而且是反复多次全表扫描。当被驱动表很大的时候,SQL就执行不出结果。
嵌套循环被驱动表走索引只能走INDEX UNIQUE SCAN或者INDEX RANGE SCAN。
嵌套循环被驱动表不能走TABLE ACCESS FULL,不能走INDEX FULL SCAN,不能走INDEX SKIP SCAN,也不能走INDEX FAST FULL SCAN。
嵌套循环被驱动表的连接列基数应该很高。如果被驱动表连接列的基数很低,那么被驱动表就不应该走索引,这样一来被驱动表就只能进行全表扫描了,但是被驱动表也不能走全表扫描。
两表关联返回少量数据才能走嵌套循环。前面提到,嵌套循环被驱动表必须走索引,如果两表关联,返回100万行数据,那么被驱动表走索引就会产生100万次回表。回表一般是单块读,这个时候SQL性能极差,所以两表关联返回少量数据才能走嵌套循环。

二.HASH连接(HASH JOIN)

上面提到,两表关联返回少量数据应该走嵌套循环,两表关联返回大量数据应该走HASH连接。
HASH连接的算法:两表等值关联,返回大量数据,将较小的表选为驱动表,将驱动表的“select列和join列”读入PGA中的work area,然后对驱动表的连接列进行hash运算生成hash table,当驱动表的所有数据完全读入PGA中的work area之后,再读取被驱动表(被驱动表不需要读入PGA中的work area),对被驱动表的连接列也进行hash运算,然后到PGA中的work area去探测hash table,找到数据就关联上,没找到数据就没关联上。哈希连接只支持等值连接。

再次强调,嵌套循环被驱动表需要扫描多次,HASH连接的被驱动表只需要扫描一次。
HASH连接,执行计划中的Used-Mem表示HASH连接消耗了多少PGA,当驱动表太大、PGA不能完全容纳驱动表时,驱动表就会溢出到临时表空间,进而产生磁盘HASH连接,这时候HASH连接性能会严重下降。嵌套循环不需要消耗PGA。
嵌套循环每循环一次,会将驱动表连接列传值给被驱动表的连接列,也就是说嵌套循环会进行传值。HASH连接没有传值的过程。在进行HASH连接的时候,被驱动表的连接列会生成HASH值,到PGA中去探测驱动表所生成的hash table。HASH连接的驱动表与被驱动表的连接列都不需要创建索引。
OLTP环境一般是高并发小事物居多,此类SQL返回结果很少,SQL执行计划多以嵌套循环为主,因此OLTP环境SGA设置较大,PGA设置较小(因为嵌套循环不消耗 PGA)。而OLAP环境多数SQL都是大规模的ETL,此类SQL返回结果集很多,SQL执行计划通常以HASH连接为主,往往要消耗大量PGA,所以OLAP系统PGA设置较大。

三.排序合并连接(SORT MERGE JOIN)

上文提到HASH连接主要用于处理两表等值关联返回大量数据。
排序合并连接主要用于处理两表非等值关联,比如>,>=,<, <=,< ,但是不能用于instr、substr、like、regexp_like关联,instr、substr、like、regexp_like关联只能走嵌套循环。现有如下SQL。

select * from a,b where a.id>=b.id;

A表有10万条数据,B表有20万条数据,A表与B表的ID列都是从1开始每次加1。该SQL是非等值连接,因此不能进行HASH连接。
假如该SQL走的是嵌套循环,A作为驱动表,B作为被驱动表,那么B表会被扫描10万次。前文提到,嵌套循环被驱动表连接列要包含在索引中,那么B表的ID列需要创建一个索引,嵌套循环会进行传值,当A表通过ID列传值超过10000的时候,B表通过ID列的索引返回数据每次都会超过10000条,这个时候会造成B表大量回表。所以该SQL不能走嵌套循环,只能走排序合并连接。
排序合并连接的算法:两表关联,先对两个表根据连接列进行排序,将较小的表作为驱动表(Oracle官方认为排序合并连接没有驱动表,笔者认为是有的),然后从驱动表中取出连接列的值,到已经排好序的被驱动表中匹配数据,如果匹配上数据,就关联成功。驱动表返回多少行,被驱动表就要被匹配多少次,这个匹配的过程类似嵌套循环,但是嵌套循环是从被驱动表的索引中匹配数据,而排序合并连接是在内存中(PGA中的work area)匹配数据。

如果两表是等值关联,一般不建议走排序合并连接。因为排序合并连接需要将两个表放入PGA中,而HASH连接只需要将驱动表放入PGA中,排序合并连接与HASH连接相比,需要耗费更多的PGA。即使排序合并连接中有一个表走的是INDEX FULL SCAN,另外一个表也需要放入PGA中,而这个表往往是大表,如果走HASH连接,大表会作为被驱动表,是不会被放入PGA中的。因此,两表等值关联,要么走NL(返回数据量少),要么走HASH(返回数据量多),一般情况下不要走SMJ。
在这里插入图片描述

四.笛卡尔连接(CARTESIAN JOIN)

两个表关联没有连接条件的时候会产生笛卡尔积,这种表连接方式就叫笛卡尔连接。
笛卡尔连接会返回两个表的乘积,比如a表有4条数据,b表有20条数据,两个表进行笛卡尔连接之后就回返回80条数据。
在多表关联的时候,两个表没有直接关联条件,但是优化器错误的把某个表返回的Rows算为一行(必须是一行),这时候也可能发生笛卡尔连接。

五.标量子查询(SCALAR SUBQUERY)

当一个子查询介于select与from之间,这种子查询就叫标量子查询。
例子:

select e.ename,
		  e.sal,
		  (select d.dname from dept d where d.deptno=e.deptno) dname
  from emp e;

标量子查询类似一个天然的嵌套循环,而且驱动表固定为主表。大家是否记得,嵌套循环被驱动表的连接列必须包含在索引中。同理,标量子查询中子查询的表连接列也必须包含在索引中。
建议在工作中,尽量避免使用标量子查询,假如主表返回大量数据,主表的连接列基数很高,那么子查询中的表会被多次扫描,从而严重影响SQL性能。如果主表数据量小,或者主表的连接列基数很低,那么这个时候我们也可以使用标量子查询,但是记得要给子查询中表的连接列建立索引。
当SQL里面有标量子查询,我们可以将标量子查询等价改写为外连接,从而使它们可以进行HASH连接。为什么要将标量子查询改写为外连接而不是内连接呢?因为标量子查询是一个传值的过程,如果主表传值给子查询,子查询没有查询到数据,这个时候会显示NULL。如果将标量子查询改写为内连接,会丢失没有关联上的数据。

在12c中,简单的标量子查询会被优化器等价改写为外连接

六.半连接(SEMI JOIN)

两表关联只返回一个表的数据就叫半连接。半连接一般就是指的in和exists。在SQL优化中,半连接的优化是最为复杂的。

1.半连接等价改写

in和exists一般情况下都可以进行等价改写
半连接in的写法如下:

select * from dept where deptno in (select deptno from emp);

半连接exists的写法如下

select * from dept where exists (select null from emp where dept.deptno=emp.deptno);

如果半连接中主表属于1的关系,子表(子查询中的表)属于n的关系,我们在改写为内连接的时候,需要加上GROUPBY去重。注意:这个时候半连接性能高于内连接。
如果半连接中主表属于n的关系,子表(子查询中的表)属于1的关系,我们在改写为内连接的时候,就不需要去重了。注意:这个时候半连接与内连接性能一样。
如果半连接中主表属于n的关系,子表(子查询中的表)也属于n的关系,这时我们可以先对子查询去重,将子表转换为1的关系,然后再关联,千万不能先关联再去重。

2.控制半连接执行计划

示例SQL

explain plan for select * from dept where deptno in (select deptno from emp);

执行计划中DEPT和EMP是采用排序合并连接进行关联的。
现在加HINT,让DEPT和EMP进行嵌套循环连接,同时让DEPT当驱动表

select /*+ use_nl(emp@a,dept) leading(dept) */
 *
  from dept
 where deptno in (select /*+ qb_name(a) */ deptno from emp);

如果不想使用qb_name这个hint 也可以参考以下操作

explain plan for select * from dept where deptno in (select deptno from emp);
select * from table(dbms_xplan.display(null,null,'advanced -projection -outline -predicate'));

现在我们让DEPT与EMP进行HASH连接,同时让EMP作为驱动表
select /*+ use_hash(dept,emp@sel$2) leading(emp@sel$2) */
*
from dept
where deptno in (select deptno from emp);

让EMP表作为驱动表之后,CBO先对EMP进行了去重(SORT UNIQUE)操作,这里CBO其实对该SQL进行了等价改写,将半连接等价改写为内连接(因为执行计划中没有SEMI关键字),在改写的过程中,因为EMP属于N的关系,所以对EMP进行了去重。

七.反连接

两表关联只返回主表数据,而且只返回主表与子表没关联上的数据,这种连接就叫反连接。反连接一般就是指not in和not exists
反连接等价改写
not in的写法如下

select * from dept where deptno not in (select deptno from emp);

not exists的写法如下

select * from dept where not exists (select null from emp where dept.deptno = emp.deptno);

需要注意的是,not in里面如果有null,整个查询会返回空,而in里面有null,查询不受null影响,例子如下

select * from dept where deptno not in (10,null);

所以在将not exists等价改写为not in的时候,要注意null。一般情况下,如果反连接采用not in写法,我们需要在where条件中剔除null

select * from dept where deptno not in (select deptno from emp where deptno is not null);

八.FILTER

如果子查询(in/exists/not in/not exists)没能展开(unnest),在执行计划中就会产生FILTER,FILTER类似嵌套循环,FILTER的算法与标量子查询一模一样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2247531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序25- iconfont 字体图标的使用

项目中使用到图标&#xff0c;一般由公司设计进行设计&#xff0c;设计好后上传到阿里巴巴矢量图标库 日常开发过程中&#xff0c;也可以通过 iconfont 图标库下载使用自带的图标 补充&#xff1a;使用 iconfont 图标库报错&#xff1a;Failed to load font 操作步骤&#xff…

Java基于SpringBoot+Vue的藏区特产销售平台

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

selinux及防火墙

selinux说明 SELinux 是 Security-Enhanced Linux 的缩写&#xff0c;意思是安全强化的 linux 。 SELinux 主要由美国国家安全局&#xff08; NSA &#xff09;开发&#xff0c;当初开发的目的是为了避免资源的误用。 httpd进程标签&#xff08;/usr/share/nginx/html &#…

详细探索xinput1_3.dll:功能、问题与xinput1_3.dll丢失的解决方案

本文旨在深入探讨xinput1_3.dll这一动态链接库文件。首先介绍其在计算机系统中的功能和作用&#xff0c;特别是在游戏和输入设备交互方面的重要性。然后分析在使用过程中可能出现的诸如文件丢失、版本不兼容等问题&#xff0c;并提出相应的解决方案&#xff0c;包括重新安装相关…

生成对抗网络模拟缺失数据,辅助PAMAP2数据集仿真实验

PAMAP2数据集是一个包含丰富身体活动信息的数据集&#xff0c;它为我们提供了一个理想的平台来开发和测试HAR模型。本文将从数据集的基本介绍开始&#xff0c;逐步引导大家通过数据分割、预处理、模型训练&#xff0c;到最终的性能评估&#xff0c;在接下来的章节中&#xff0c…

IEC61850读服务器目录命令——GetServerDirectory介绍

IEC61850标准中的GetServerDirectory命令是变电站自动化系统中非常重要的一个功能&#xff0c;它主要用于读取服务器的目录信息&#xff0c;特别是服务器的逻辑设备节点&#xff08;LDevice&#xff09;信息。以下是对GetServerDirectory命令的详细介绍。 目录 一、命令功能 …

基于CNN+RNNs(LSTM, GRU)的红点位置检测(pytorch)

1 项目背景 需要在图片精确识别三跟红线所在的位置&#xff0c;并输出这三个像素的位置。 其中&#xff0c;每跟红线占据不止一个像素&#xff0c;并且像素颜色也并不是饱和度和亮度极高的红黑配色&#xff0c;每个红线放大后可能是这样的。 而我们的目标是精确输出每个红点的…

前端:JavaScript (学习笔记)【2】

目录 一&#xff0c;数组的使用 1&#xff0c;数组的创建 [ ] 2&#xff0c;数组的元素和长度 3&#xff0c;数组的遍历方式 4&#xff0c;数组的常用方法 二&#xff0c;JavaScript中的对象 1&#xff0c;常用对象 &#xff08;1&#xff09;String和java中的Stri…

全面解析多种mfc140u.dll丢失的解决方法,五种方法详细解决

当你满心期待地打开某个常用软件&#xff0c;却突然弹出一个错误框&#xff0c;提示“mfc140u.dll丢失”&#xff0c;那一刻&#xff0c;你的好心情可能瞬间消失。这种情况在很多电脑用户的使用过程中都可能出现。无论是游戏玩家还是办公族&#xff0c;面对这个问题都可能不知所…

STM32总体架构简单介绍

目录 一、引言 二、STM32的总体架构 1、三个被动单元 &#xff08;1&#xff09;内部SRAM &#xff08;2&#xff09;内部闪存存储器 &#xff08;3&#xff09;AHB到APB的桥&#xff08;AHB to APBx&#xff09; 2、四个主动&#xff08;驱动&#xff09;单元 &#x…

【PHP】 环境以及插件的配置,自学笔记(一)

文章目录 环境的准备安装 XAMPPWindowMacOS 配置开发环境Vscode 关于 PHP 的插件推荐Vscode 配置 php 环境Apache 启动Hello php配置热更新 参考 环境的准备 下载 XAMPP , 可以从 官网下载 https://www.apachefriends.org/download.html 安装 XAMPP XAMPP 是一个跨平台的集成开…

跟着问题学5——深度学习中的数据集详解(1)

深度学习数据集的创建与读取 数据 &#xff08;计算机术语&#xff09; 数据(data)是事实或观察的结果&#xff0c;是对客观事物的逻辑归纳&#xff0c;是用于表示客观事物的未经加工的的原始素材。 数据可以是连续的值&#xff0c;比如声音、图像&#xff0c;称为模拟数据。…

实验-Linux文件系统和磁盘管理

操作1 远程连接Linux系统 下述连接方式2选一即可。 使用xshell工具连接Linux系统。打开xshell&#xff0c;新建连接&#xff0c;将主机ip修改为实际Linux系统的ip(ifconfig命令查看)&#xff0c;可以新建多个xshell会话&#xff0c;使用不同的用户名登录&#xff0c;方便后续…

GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性

产品描述 GPTZero 是一款先进的AI文本检测工具&#xff0c;专为识别由大型语言模型&#xff08;如ChatGPT、GPT-4、Bard等&#xff09;生成的文本而设计。它通过分析文本的复杂性和一致性&#xff0c;判断文本是否可能由人类编写。GPTZero 已经得到了超过100家媒体机构的报道&…

Apple Vision Pro开发003-PolySpatial2.0新建项目

unity6.0下载链接:Unity 实时开发平台 | 3D、2D、VR 和 AR 引擎 一、新建项目 二、导入开发包 com.unity.polyspatial.visionos 输入版本号 2.0.4 com.unity.polyspatial&#xff08;单独导入&#xff09;&#xff0c;或者直接安装 三、对应设置 其他的操作与之前的版本相同…

百度在下一盘大棋

这两天世界互联网大会在乌镇又召开了。 我看到一条新闻&#xff0c;今年世界互联网大会乌镇峰会发布“2024 年度中国互联网企业创新发展十大典型案例”&#xff0c;百度文心智能体平台入选。 这个智能体平台我最近也有所关注&#xff0c;接下来我就来讲讲它。 百度在下一盘大棋…

038集——quadtree(CAD—C#二次开发入门)

效果如下&#xff1a; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using System; using System.Collections.Generic; using System.Linq; using System.T…

深入探讨 Puppeteer 如何使用 X 和 Y 坐标实现鼠标移动

背景介绍 现代爬虫技术中&#xff0c;模拟人类行为已成为绕过反爬虫系统的关键策略之一。无论是模拟用户点击、滚动&#xff0c;还是鼠标的轨迹移动&#xff0c;都可以为爬虫脚本带来更高的“伪装性”。在众多的自动化工具中&#xff0c;Puppeteer作为一个无头浏览器控制库&am…

RabbitMQ2:介绍、安装、快速入门、数据隔离

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Linux 下进程基本概念与状态

文章目录 一、进程的定义二、 描述进程-PCBtask_ struct内容分类 三、 进程状态 一、进程的定义 狭义定义&#xff1a;进程是正在运行的程序的实例&#xff08;an instance of a computer program that is being executed&#xff09;。广义定义&#xff1a;进程是一个具有一定…