【MySQL精通之路】SQL优化(1)-查询优化(8)-嵌套联接优化
主博客:
【MySQL精通之路】SQL优化(1)-查询优化-CSDN博客
上一篇:
【MySQL精通之路】SQL优化(1)-查询优化(7)-嵌套循环联接-CSDN博客
下一篇:
【MySQL精通之路】SQL优化(1)-查询优化(9)-外部联接优化-CSDN博客
与SQL标准相比,table_factor的语法得到了扩展。后者只接受table_reference,而不接受一对括号内的结果。如果我们将table_reference项列表中的每个逗号视为等效于内部联接,那么这是一个保守的扩展。
例如:
SELECT * FROM t1 LEFT JOIN (t2, t3, t4)ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)
相当于:
SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)
在MySQL中,CROSS JOIN在语法上等同于INNER JOIN;它们可以相互替换。
在标准SQL中,它们是不等价的。INNER JOIN与ON子句一起使用;否则使用CROSS JOIN。
通常,在只包含内部联接操作的联接表达式中,可以忽略圆括号。
例如下面联接表达式:
t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.b=t3.b OR t2.b IS NULL)ON t1.a=t2.a
删除括号并向左分组操作后,该联接表达式将转换为以下表达式:
(t1 LEFT JOIN t2 ON t1.a=t2.a) LEFT JOIN t3ON t2.b=t3.b OR t2.b IS NULL
然而,这两种表述并不等同。
为此,假设表t1、t2和t3具有以下状态:
Table t1 contains rows (1), (2)
Table t2 contains row (1,101)
Table t3 contains row (101)
在这种情况下,第一个表达式返回包括行(1,1,101,101), (2,NULL,NULL,NULL),的结果集,而第二个表达式返回行(1,1,101,101), (2,NULL,NULL,101)
mysql> SELECT *FROM t1LEFT JOIN(t2 LEFT JOIN t3 ON t2.b=t3.b OR t2.b IS NULL)ON t1.a=t2.a;
+------+------+------+------+
| a | a | b | b |
+------+------+------+------+
| 1 | 1 | 101 | 101 |
| 2 | NULL | NULL | NULL |
+------+------+------+------+mysql> SELECT *FROM (t1 LEFT JOIN t2 ON t1.a=t2.a)LEFT JOIN t3ON t2.b=t3.b OR t2.b IS NULL;
+------+------+------+------+
| a | a | b | b |
+------+------+------+------+
| 1 | 1 | 101 | 101 |
| 2 | NULL | NULL | 101 |
+------+------+------+------+
在以下示例中,外联操作与内联操作一起使用:
t1 LEFT JOIN (t2, t3) ON t1.a=t2.a
该表达式无法转换为以下表达式:
t1 LEFT JOIN t2 ON t1.a=t2.a, t3
对于给定的表状态,这两个表达式返回不同的行集:
mysql> SELECT *FROM t1 LEFT JOIN (t2, t3) ON t1.a=t2.a;
+------+------+------+------+
| a | a | b | b |
+------+------+------+------+
| 1 | 1 | 101 | 101 |
| 2 | NULL | NULL | NULL |
+------+------+------+------+mysql> SELECT *FROM t1 LEFT JOIN t2 ON t1.a=t2.a, t3;
+------+------+------+------+
| a | a | b | b |
+------+------+------+------+
| 1 | 1 | 101 | 101 |
| 2 | NULL | NULL | 101 |
+------+------+------+------+
因此,如果我们在带有外联运算符的联接表达式中省略括号,我们可能会更改原始表达式的结果集。
更确切地说,我们不能忽略左外部联接运算的右操作数和右联接运算的左操作数中的括号。换句话说,对于外部联接操作的内部表表达式,我们不能忽略括号。可以忽略其他操作数(外部表的操作数)的括号。
以下表达式:
(t1,t2) LEFT JOIN t3 ON P(t2.b,t3.b)
对于任何表t1、t2、t3和属性t2.b和t3.b上的任何条件P,等价于该表达式:
t1, t2 LEFT JOIN t3 ON P(t2.b,t3.b)
每当联接表达式(joined_table)中联接操作的执行顺序不是从左到右时,我们就讨论嵌套联接。请考虑以下查询:
SELECT * FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.b=t3.b) ON t1.a=t2.aWHERE t1.a > 1SELECT * FROM t1 LEFT JOIN (t2, t3) ON t1.a=t2.aWHERE (t2.b=t3.b OR t2.b IS NULL) AND t1.a > 1
这些查询被认为包含这些嵌套联接:
t2 LEFT JOIN t3 ON t2.b=t3.b
t2, t3
第一个查询中,嵌套联接是通过左联接操作形成的。
第二个查询中,它是用内部联接操作形成的。
第一个查询,括号可以省略:联接表达式的语法结构规定了联接操作的相同执行顺序。
第二个查询,不能省略括号,尽管这里的联接表达式在没有括号的情况下可以被明确地解释。
在我们的扩展语法中,第二个查询的(t2,t3)中的括号是必需的,尽管理论上可以在没有括号的情况下解析查询:我们仍然会有明确的查询语法结构,因为LEFT JOIN ON扮演着表达式(t2,t3)的左右分隔符的角色。
前面的例子说明了以下几点:
对于只涉及内部联接(而不涉及外部联接)的联接表达式,可以删除圆括号,并从左到右计算联接。事实上,表可以按任何顺序进行评估。
博主ps,你可以理解为内联接查询(求交集的过程),就是先把所有表都先求交集拼接起来,先拼谁都差不多
通常,对于外部联接或与内部联接混合的外部联接,情况并非如此。删除括号可能会改变结果。
具有嵌套外部联接的查询以与具有内部联接的查询相同的管道方式执行。
更确切地说,利用了嵌套循环连接算法的变体。调用嵌套循环联接执行查询的算法
(请参阅“嵌套循环联接算法”)。
假设对3个表T1、T2、T3的联接查询具有以下形式:
SELECT * FROM T1 INNER JOIN T2 ON P1(T1,T2)INNER JOIN T3 ON P2(T2,T3)WHERE P(T1,T2,T3)
这里,P1(T1,T2)和P2(T3,T3)是一些连接条件(关于表达式),而P(T1,T2,T3)是表T1、T2、T3的列上的条件。
嵌套循环联接算法将以以下方式执行此查询:
FOR each row t1 in T1 {FOR each row t2 in T2 such that P1(t1,t2) {FOR each row t3 in T3 such that P2(t2,t3) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}}}
}
符号t1 || t2 || t3 表示通过串联行t1、t2和t3的列而构造的行。
在下面的一些示例中,NULL表示该表的每一列的行。
例如:
t1 || t2 || NULL表示通过串联行t1和t2的列而构造的行,并且对于t3的每一列设为NULL。
这样的行被称为NULL补码。
现在考虑一个具有嵌套外部联接的查询:
SELECT * FROM T1 LEFT JOIN(T2 LEFT JOIN T3 ON P2(T2,T3))ON P1(T1,T2)
WHERE P(T1,T2,T3)
对于此查询,请修改嵌套循环模式以获得:
FOR each row t1 in T1 {BOOL f1:=FALSE;FOR each row t2 in T2 such that P1(t1,t2) {BOOL f2:=FALSE;FOR each row t3 in T3 such that P2(t2,t3) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}f2=TRUE;f1=TRUE;}IF (!f2) {IF P(t1,t2,NULL) {t:=t1||t2||NULL; OUTPUT t;}f1=TRUE;}}IF (!f1) {IF P(t1,NULL,NULL) {t:=t1||NULL||NULL; OUTPUT t;}}
}
通常,对于外部联接操作中第一个内部表的任何嵌套循环,都会引入一个标志,该标志在循环之前为false,在循环之后检查状态。
当外部表中的当前行与表示内部操作的表匹配时,该标志为true。
如果在循环周期结束时,标志仍然为false,则未找到外部表的当前行的匹配项。在这种情况下,该行由内部表的列的NULL值补充。
结果行 被传递给输出 或下一个嵌套循环,但前提是该行满足所有外部联接的联接条件。
在该示例中,嵌入了由以下表达式表示的外部联接表:
(T2 LEFT JOIN T3 ON P2(T2,T3))
对于具有内部联接的查询,优化器可以选择不同顺序的嵌套循环,例如以下循环:
FOR each row t3 in T3 {FOR each row t2 in T2 such that P2(t2,t3) {FOR each row t1 in T1 such that P1(t1,t2) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}}}
}
对于具有外部联接的查询,优化器只能选择这样一种顺序,即外部表的循环先于内部表的循环。因此,对于具有外部联接的查询,只有一个嵌套顺序是可能的。
对于下面的查询,优化器评估两个不同的嵌套。
在两个嵌套中,T1都必须在外循环中处理,因为它用于外联接。
T2和T3用于内部联接,因此联接必须在内部循环中处理。
然而,由于连接是内部连接,T2和T3可以按任意顺序进行处理。
SELECT * T1 LEFT JOIN (T2,T3) ON P1(T1,T2) AND P2(T1,T3)WHERE P(T1,T2,T3)
一个嵌套计算T2,然后计算T3:
FOR each row t1 in T1 {BOOL f1:=FALSE;FOR each row t2 in T2 such that P1(t1,t2) {FOR each row t3 in T3 such that P2(t1,t3) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}f1:=TRUE}}IF (!f1) {IF P(t1,NULL,NULL) {t:=t1||NULL||NULL; OUTPUT t;}}
}
另一个嵌套计算T3,然后计算T2:
FOR each row t1 in T1 {BOOL f1:=FALSE;FOR each row t3 in T3 such that P2(t1,t3) {FOR each row t2 in T2 such that P1(t1,t2) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}f1:=TRUE}}IF (!f1) {IF P(t1,NULL,NULL) {t:=t1||NULL||NULL; OUTPUT t;}}
}
在讨论内联接的嵌套循环算法时,我们省略了一些细节,这些细节可能会对查询执行的性能产生巨大影响。我们没有提到所谓的“下推”条件。假设我们的WHERE条件P(T1,T2,T3)可以用一个连接公式表示:
P(T1,T2,T2) = C1(T1) AND C2(T2) AND C3(T3).
在这种情况下,MySQL实际上使用以下嵌套循环算法来执行具有内部联接的查询:
FOR each row t1 in T1 such that C1(t1) {FOR each row t2 in T2 such that P1(t1,t2) AND C2(t2) {FOR each row t3 in T3 such that P2(t2,t3) AND C3(t3) {IF P(t1,t2,t3) {t:=t1||t2||t3; OUTPUT t;}}}
}
你可以看到,连接词C1(T1)、C2(T2)、C3(T3)中的每一个都被从最内部的循环推到最外部的循环,在那里它可以被评估。如果C1(T1)是一个非常严格的条件,则此条件下推可以大大减少从表T1传递到内部循环的行数。因此,查询的执行时间可能会大大提高。
博主PS:
这里意思是如果可以减少外层for循环的查询次数,将大大减少内层for循环的循环次数。这就是条件下推的意思。也就是将内层for循环的匹配条件推到外层判断。这样如果不匹配的话,之间终结外层循环。
对于具有外部联接的查询,只有在发现外部表中的当前行在内部表中匹配后,才检查WHERE条件。
因此,将判断条件推出内部嵌套循环的优化不能直接应用于具有外部联接的查询。
在这里,我们必须介绍 条件下推在标志打开时有保留的断言 。
回想一下这个带有外部联接的示例:
P(T1,T2,T3)=C1(T1) AND C(T2) AND C3(T3)
例如,使用受限的下推条件嵌套循环算法如下所示:
FOR each row t1 in T1 such that C1(t1) {BOOL f1:=FALSE;FOR each row t2 in T2such that P1(t1,t2) AND (f1?C2(t2):TRUE) {BOOL f2:=FALSE;FOR each row t3 in T3such that P2(t2,t3) AND (f1&&f2?C3(t3):TRUE) {IF (f1&&f2?TRUE:(C2(t2) AND C3(t3))) {t:=t1||t2||t3; OUTPUT t;}f2=TRUE;f1=TRUE;}IF (!f2) {IF (f1?TRUE:C2(t2) && P(t1,t2,NULL)) {t:=t1||t2||NULL; OUTPUT t;}f1=TRUE;}}IF (!f1 && P(t1,NULL,NULL)) {t:=t1||NULL||NULL; OUTPUT t;}
}
通常,可以从诸如P1(T1,T2)和P(T2,T3)的联接条件中提取下推。
在这种情况下,下推断言由一个标志保护,该标志防止检查断言时由相应的外部联接操作生成的NULL补码行。
通过键从一个内部表访问同一嵌套联接中的另一个表是被禁止的,如果它是由WHERE条件中的谓词引发的
相关文章:

【MySQL精通之路】SQL优化(1)-查询优化(8)-嵌套联接优化
主博客: 【MySQL精通之路】SQL优化(1)-查询优化-CSDN博客 上一篇: 【MySQL精通之路】SQL优化(1)-查询优化(7)-嵌套循环联接-CSDN博客 下一篇: 【MySQL精通之路】SQL优化(1)-查询优化(9)-外部联接优化-CSDN博客 与SQL标准相比,…...

30V降8V、12V、24V3.5A车充降压芯片IC H4112 5V-30V
H4112确实是一款功能强大的异步降压型DC-DC转换器,它具备多种出色的特性和优势,使得它在电源管理领域有着广泛的应用。以下是对H4112主要特性和功能的详细解释: 内置30V耐压MOS: H4112内部集成了30V耐压的MOS管,这有…...

保护共享资源的方法(互斥锁)
我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》࿱…...

树的非递归遍历(层序)
层序是采用队列的方式来遍历的 就比如说上面这颗树 他层序的就是:1 24 356 void LevelOrder(BTNode* root) {Que q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front QueueFront(&q);QueuePop(&q);print…...

解决SpringBoot使用@Transactional进行RestTemplate远程调用导致查询数据记录为null的bug
开启事务过程中,如果远程调用查询当前已经开启但没有提交的事务,就会查不到数据。 示例代码 import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; import o…...

pl/sql基础语法操作
oracle pl/sql语言(procedural language/sql)是结合了结构化查询与oracle自身过程控制为一体的强大语言。 语法执行块 语法结构: [ declare 可选 声明变量部分--declaration statements (1);]begin --执行部分--executable statements (2)…...

Vue 父组件向子组件传递数据
1、在子组件中,你需要声明你期望从父组件接收哪些props。这可以通过props选项完成,可以是一个数组或对象形式: export default {props: [message],props:{message:String }props: {message: String, // 类型检查count: {type: Nu…...

二十五、openlayers官网示例CustomOverviewMap解析——实现鹰眼地图、预览窗口、小窗窗口地图、旋转控件
官网demo地址: Custom Overview Map 这个示例展示了如何在地图上增加一个小窗窗口的地图并跟随着地图的旋转而旋转视角。 首先加载了一个地图。其中 DragRotateAndZoom是一个交互事件,它可以实现按住shift键鼠标拖拽旋转地图。 const map new Map({int…...

K8S Secret管理之SealedSecrets
1 关于K8S Secret 我们通常将应用程序使用的密码、API密钥保存在K8S Secret中,然后应用去引用。对于这些敏感信息,安全性是至关重要的,而传统的存储方式可能会导致密钥在存储、传输或使用过程中受到威胁,例如在git中明文存储密码…...

Gone框架介绍25 - Redis模块参考文档
文章目录 Redis 参考文档配置项import 和 bury使用分布是缓存 redis.Cache接口定义使用示例 使用分布式锁 redis.Locker接口定义使用示例 操作Key,使用 redis.Key接口定义 使用 Provider 注入 redis 接口使用示例 直接使用redis连接池接口定义使用示例 Redis 参考文…...

SpringBoot前置知识02-spring注解发展史
springboot前置知识01-spring注解发展史 spring1.x spring配置只能通过xml配置文件的方式注入bean,需要根据业务分配配置文件,通过import标签关联。 spring1.2版本出现Transactional注解 <?xml version"1.0" encoding"UTF-8"?> <be…...

C++ TCP发送Socket数据
DEVC需要加入ws2_32库 #include <iostream> #include <winsock2.h>#pragma comment(lib, "ws2_32.lib")void sendData(const char* ip, int port, const char* data) {WSADATA wsaData;SOCKET sockfd;struct sockaddr_in server_addr;// 初始化Winsock…...

鸿蒙HarmonyOS开发中的易混点归纳-持续补充中
相关文章目录 鸿蒙HarmonyOS开发术语全解:小白也能看懂! 文章目录 相关文章目录前言一、build()函数和Builder装饰器?二、自定义组件和系统组件(内置组件)三、组件和页面四、自定义弹窗和其他弹窗总结 前言 一、build…...

ue引擎游戏开发笔记(45)——添加游戏音效
1.需求分析: 截至目前,我们仍然在一个无声的世界游玩游戏,所以有必要为游戏增添一些声音,例如开火声,子弹撞击声等等。 2.操作实现: 1.这是一个较为简单的功能,类似特效的实现方法,…...

202472读书笔记|《首先你要快乐,其次都是其次》——快乐至上,允许一切发生
202472读书笔记|《首先你要快乐,其次都是其次》——快乐至上,允许一切发生 《首先你要快乐,其次都是其次》作者林小仙,挺轻松的小漫画,清新的文字。 生而为人,我很抱歉,大可不必。 生活已经很难…...

8.STL中Vector容器的常见操作(附习题)
目录 1.vector的介绍 2 vector的使用 2.1 vector的定义 2.2 vector iterator 的使用 2.3 vector 空间增长问题 2.3 vector 增删查改 2.4 vector 迭代器失效问题 2.5 vector 在OJ中的使用 1.vector的介绍 vector是表示可变大小数组的序列容器。 就像数组一样࿰…...

5.23小结
1.java项目创新 目前想添加一个自动回复的功能和设置验证方式有(允许任何人添加,禁止添加,设置回答问题添加,普通验证添加) 目前只完成画好前端界面,前端发送请求,还有表的修改 因为涉及表字…...

文心一言 VS 讯飞星火 VS chatgpt (265)-- 算法导论20.1 4题
四、假设不使用一棵叠加的度为 u \sqrt{u} u 的树,而是使用一棵叠加的度为 u 1 k u^{\frac{1}{k}} uk1的树,这里 k 是大于 1 的常数,则这样的一棵树的高度是多少?又每个操作将需要多长时间?如果要写代码…...

Flutter 中的 EditableText 小部件:全面指南
Flutter 中的 EditableText 小部件:全面指南 在Flutter中,EditableText是一个低级别的文本编辑组件,它提供了构建自定义文本编辑界面的能力。与TextField和TextFormField不同,EditableText提供了更多的灵活性,允许开发…...

H800基础能力测试
H800基础能力测试 参考链接A100、A800、H100、H800差异H100详细规格H100 TensorCore FP16 理论算力计算公式锁频安装依赖pytorch FP16算力测试cublas FP16算力测试运行cuda-samples 本文记录了H800基础测试步骤及测试结果 参考链接 NVIDIA H100 Tensor Core GPU Architecture…...

2024/5/24 Day38 greedy 435. 无重叠区间 763.划分字母区间 56. 合并区间
2024/5/24 Day38 greedy 435. 无重叠区间 763.划分字母区间 56. 合并区间 遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。如果两个维度一起考虑一定会顾此失彼。 重叠区间问题 435. 无重叠区间 题目链接 435 给定一个区间的集合 i…...

【python】使用函数名而不加括号是什么情况?
使用函数名而不加括号通常是为了表示对函数本身的引用,而不是调用函数。这种用法通常出现在下面这几种情况: 作为回调函数传递:将函数名作为参数传递给其他函数,以便在需要时调用该函数。例如,在事件处理程序或高阶函数…...

全文检索ElasticSearch简介
1 全文检索 1.1 什么是全文检索 全文检索是一种通过对文本内容进行全面索引和搜索的技术。它可以快速地在大量文本数据中查找包含特定关键词或短语的文档,并返回相关的搜索结果。全文检索广泛应用于各种信息管理系统和应用中,如搜索引擎、文档管理系统、电子邮件客户端、新闻…...

Github上传时报错The file path is empty的解决办法
问题截图 文件夹明明不是空的,却怎么都上传不上去。 解决方案: 打开隐藏文件的开关,删除原作者的.git文件 如图所示: 上传成功!...

Adobe Bridge BR v14.0.3 安装教程 (多媒体文件组织管理工具)
Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 安装教程 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 安装教程 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 安装教程 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 安装…...

嵌入式学习——3——TCP-UDP 数据交互,握手,挥手
1、更新源 cd /etc/apt/ sudo cp sources.list sources.list.save 将原镜像备份 sudo vim sources.list 将原镜像修改成阿里源/清华源,如所述 阿里源 deb http://mirrors.aliyun.com/ubuntu/ bionic main …...

【LeetCode】【3】无重复字符的最长子串(1113字)
文章目录 [toc]题目描述样例输入输出与解释样例1样例2样例3 提示Python实现滑动窗口 个人主页:丷从心 系列专栏:LeetCode 刷题指南:LeetCode刷题指南 题目描述 给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度 样…...

溪谷联运SDK功能全面解析
近期,备受用户关注的手游联运10.0.0版本上线了,不少用户也选择了版本更新,其中也再次迎来了SDK的更新。溪谷软件和大家一起盘点一下溪谷SDK的功能都有哪些吧。 一、溪谷SDK具有完整的运营功能和高度扩展性 1.登录:登录是SDK最基础…...

Vitis HLS 学习笔记--控制驱动TLP - Dataflow视图
目录 1. 简介 2. 功能特性 2.1 Dataflow Viewer 的功能 2.2 Dataflow 和 Pipeline 的区别 3. 具体演示 4. 总结 1. 简介 Dataflow视图,即数据流查看器。 DATAFLOW优化属于一种动态优化过程,其完整性依赖于与RTL协同仿真的完成。因此,…...

蓝桥杯物联网竞赛_STM32L071KBU6_关于sizo of函数产生的BUG
首先现象是我在用LORA发送信息的时候,左边显示长度是8而右边接收到的数据长度却是4 我以为是OLED显示屏坏了,又或者是我想搞创新用了const char* 类型强制转换数据的原因,结果发现都不是 void Function_SendMsg( unsigned char* data){unsi…...