【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 JOINON子句一起使用;否则使用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条件中的谓词引发的

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

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

相关文章

robosuite导入自定义机器人

目录 目的:案例一:成果展示具体步骤:URDF文件准备xml文件生成xml修改机器人构建 目的: 实现其他标准/非标准机器人的构建 案例一: 成果展示 添加机器人JAKA ZU 7 这个模型 具体步骤: URDF文件准备 从…

python-docx 在word中指定位置插入图片或表格

docx库add_picture()方法不支持对图片位置的设置 1、新建一个1行3列的表格,在中间的一列中插入图片 from docx import Document from docx.shared import Pt from docx.oxml.shared import OxmlElement from docx.enum.text import WD_ALIGN_PARAGRAPHdef add_cen…

Nacos 进阶篇---Nacos服务端怎么维护不健康的微服务实例 ?(七)

一、引言 在 Nacos 后台管理服务列表中,我们可以看到微服务列表,其中有一栏叫“健康实例数” (如下图),表示对应的客户端实例信息是否可用状态。 那Nacos服务端是怎么感知客户端的状态是否可用呢 ? 本章…

基于树的存储数据结构demo

一.简介 由于之前博主尝试Java重构redis,在redis中的的字典数据结构底层也是采用数组实现,字典中存在两个hash表,一个是用于存储数据,另一个被用于rehash扩容为前者两倍。但是我注意到了在redis的数据结构中,并没有像…

【MySQL】库的操作和表的操作

库的操作和表的操作 一、库的操作1、创建数据库(create)2、字符集和校验规则(1)查看系统默认字符集以及校验规则(2)查看数据库支持的字符集(3)查看数据库支持的字符集校验规则(4)校验…

存储+调优:存储-IP-SAN

存储调优:存储-IP-SAN 数据一致性问题 硬盘(本地,远程同步rsync) 存储设备(网络) 网络存储 不同接口的磁盘 1.速率 2.支持连接更多设备 3.支持热拔插 存储设备什么互联 千…

ARTS Week 29

Algorithm 本周的算法题为 2413. 最小偶倍数 给你一个正整数 n ,返回 2 和 n 的最小公倍数(正整数)。 示例 1:输入:n 5输出:10解释:5 和 2 的最小公倍数是 10 。 实现代码如下: con…

由于找不到mfc140u.dll,无法继续执行代码如何解决

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是电脑找不到mfc140u.dll文件。这个问题可能会导致程序无法正常运行或系统崩溃。为了解决这个问题,本文将介绍5种修复方法,帮助大家快速恢复电脑的正常运行。 一&#x…

如何理解kmp的套娃式算法啊?

概念 KMP算法,全称Knuth Morris Pratt算法 。文章大部分内容出自《数据结构与算法之美》 核心思想 假设主串是a,模式串是b 在模式串与主串匹配的过程中,当遇到不可匹配的字符的时候,对已经对比过的字符,是否能找到…

【kubernetes】多 master 高可用集群架构部署

目录 前言 一、环境部署 二、master02 节点部署 1、拷贝相关文件 2、修改配置文件 3、启动各服务并设置开机自启 4、 查看node节点状态 三、负载均衡部署 1、部署 nginx 服务 1.1 编译安装 nginx 1.2 修改 nginx 配置文件 2、部署 keepalived 服务 2.1 yum安装 ke…

通过管理系统完成商品属性维护

文章目录 1.数据库表设计1.商品属性表 2.renren-generator生成CRUD1.基本配置检查1.generator.properties2.application.yml 2.启动RenrenGeneratorApplication.java生成CRUD1.启动后访问localhost:812.生成商品属性表的crud 3.将crud代码集成到项目中1.解压,找到ma…

python科研数据可视化之折线图

例如 : 下面的配色表画出的图很好看。选择喜欢的颜色,找到代码中颜色部分进行修改即可。 代码部分已经有详细的注释,就不一一解释了。另外,如果想要坐标轴从设定的值开始就把下面代码中的范围xlim,ylim进行注释。 imp…

设计模式12——外观模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用,主要是下面的UML图可以起到大作用,在你学习过一遍以后可能会遗忘,忘记了不要紧,只要看一眼UML图就能想起来了。同时也请大家多多指教。 外观模式(Facade&a…

javaSwing购物系统项目(文档+视频+源码)

摘要 由Java swing实现的一款简单的购物程序,数据库采用的是mysql,该项目非常简单,实现了管理员对商品类型和商品的管理及用户注册登录后浏览商品、加入购物车、购买商品等功能,旨在学习Java 图形界面开发 系统实现 我们先来管理…

CF451E: Devu and Flowers(容斥原理 + 考虑反面 + golang组合模版)

题目截图 题目翻译 题目分析 正难则反,考虑所有不符合的例子 由于n很小,所以可以状态压缩二进制遍历完全部不符合例子的组合 对于不符合的例子,假设其中第i个不符合,那么就消耗掉fi 1个球 以此类推,减剩下s2个球 这时…

一剪梅-答赠云安客刘自果

当众网友看了笔者“边吸氧边动鼠标”的短视频之后,纷纷发来微信问候。其中我的远房亲戚,那个正在潜心写作数十万字的长篇纪实文学《川江向东流》的66岁贤弟刘自果(号云安客,亦称自果居士),发来微信鼓励我&a…

【Vue】性能优化

使用 key 对于通过循环生成的列表,应给每个列表项一个稳定且唯一的 key,这有利于在列表变动时,尽量少的删除和新增元素。 使用冻结的对象 冻结的对象(Object.freeze(obj))不会被响应化,不可变。 使用函…

【Linux】网络层——IP协议

欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:Linux 目录 👉🏻IP协议基本概念👉🏻IP的协议头格式👉🏻IP协议的网段划分五类IP地址子…

mysql数据库主从复制,搭建从库

1 期望效果 假设我们现在有两个服务器,两个服务器都有数据库,然后我们命名一个叫主数据库(Master),一个叫从数据库(Slave) 数据备份和容灾:通过主从复制,可以将主数据库…

618有什么宠物空气净化器推荐?希喂FreAir Lite宠物空气净化器真实体验

一、宠物空气净化器的必要性 掉毛季又来了,猫咪的毛发满天飞,怎么办?我家里的猫咪一到换毛季就掉满地的毛发,尤其喜欢在家里奔跑打闹,结果整个房间都是毛。为了减少家里空气中的浮毛,你都做过哪些努力呢&a…