【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…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...