热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【ACDU翻译】8.2.1.8嵌套联接优化

表达联接的语法允许嵌套联接。以下讨论引用了[第13.2.10.2节“JOIN子句”中](https:dev.mysql.comdocrefman8.0enjoin.

表达联接的语法允许嵌套联接。以下讨论引用了第13.2.10.2节“ JOIN子句”中描述的联接语法 。

table_factor与SQL标准相比,的 语法得到了扩展。后者仅接受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

在除去括号并在左侧进行分组操作之后,该join表达式将转换为该表达式:

(t1 LEFT JOIN t2 ON t1.a=t2.a) LEFT JOIN t3
ON t2.b=t3.b OR t2.b IS NULL

但是,这两种表达方式并不相同。看到这一点,假设表t1t2以及t3具有以下状态:



  • 表格t1包含行 (1)(2)

  • t2包含行 (1,101)

  • t3包含行 (101)

在这种情况下,第一个表达式返回结果集包括行(1,1,101,101)(2,NULL,NULL,NULL),而第二表达式返回的行(1,1,101,101)(2,NULL,NULL,101)

mysql> SELECT *
FROM t1
LEFT 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 t3
ON 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,t3P属性t2.b 以及条件而言 ,此表达式均等效于此表达式 t3.b

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.a
WHERE t1.a > 1
SELECT * FROM t1 LEFT JOIN (t2, t3) ON t1.a=t2.a
WHERE (t2.b=t3.b OR t2.b IS NULL) AND t1.a > 1

这些查询被认为包含以下嵌套联接:

t2 LEFT JOIN t3 ON t2.b=t3.b
t2, t3

在第一个查询中,嵌套联接是通过左联接操作形成的。在第二个查询中,它是通过内部联接操作形成的。

在第一个查询中,可以省略括号:join表达式的语法结构规定了join操作的相同执行顺序。对于第二个查询,尽管可以在没有括号的情况下明确地解释此处的联接表达式,但是不能省略括号。在扩展语法中,(t2, t3)需要第二个查询的括号,尽管从理论上讲可以在没有括号的情况下对其进行解析:由于查询LEFT JOINON 在表达式的左右定界符中扮演着角色,因此查询仍然具有明确的句法结构(t2,t3)

前面的示例演示了以下几点:



  • 对于仅涉及内部联接(而不涉及外部联接)的联接表达式,可以删除括号并以从左到右的方式评估联接。实际上,可以按任何顺序评估表。

  • 通常,对于外部联接或与内部联接混合的外部联接,情况并非如此。删除括号可能会改变结果。

具有嵌套外部联接的查询的执行方式与具有内部联接的查询的执行方式相同。更确切地说,利用了嵌套循环联接算法的一种变体。调用嵌套循环联接执行查询的算法(请参见第8.2.1.7节“嵌套循环联接算法”)。假设对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)and P2(T3,T3)是一些联接条件(在表达式上),而P(T1,T2,T3)条件是在table的列上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表示通过连接的行的列构成的行 t1t2t3。在以下某些示例中, NULL出现表名的地方表示该表NULL的每一列都使用一行。例如,t1||t2||NULL 表示通过将行t1和的列t2以及 NULL的每一列 串联而构造的行t3。据说这样的行是 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;
}
}
}

通常,对于外部联接操作中第一个内部表的任何嵌套循环,都会引入一个标志,该标志在循环前关闭并在循环后检查。当针对外部表中的当前行找到表示内部操作数的表中的匹配项时,将打开该标志。如果在循环周期结束时该标志仍处于关闭状态,则未找到外部表的当前行的匹配项。在这种情况下,该行由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必须在外部循环中进行处理,因为它在外部联接中使用。T2T3在内部联接中使用,因此联接必须在内部循环中进行处理。但是,由于联接是内部联接,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 传递给内部循环的行数。结果,查询的执行时间可以大大改善。

对于具有外部联接的查询,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 T2
such that P1(t1,t2) AND (f1?C2(t2):TRUE) {
BOOL f2:=FALSE;
FOR each row t3 in T3
such 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条件谓词引起的,则禁止通过一个键在同一个嵌套联接中从一个内部表访问另一个内部表。



推荐阅读
  • SQL 连接详解与应用
    本文详细介绍了 SQL 连接的概念、分类及实际应用,包括内连接、外连接、自连接等,并提供了丰富的示例代码。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 本文总结了在SQL Server数据库中编写和优化存储过程的经验和技巧,旨在帮助数据库开发人员提升存储过程的性能和可维护性。 ... [详细]
  • 本文详细介绍了数据库并发控制的基本概念、重要性和具体实现方法。并发控制是确保多个事务在同时操作数据库时保持数据一致性的关键机制。文章涵盖了锁机制、多版本并发控制(MVCC)、乐观并发控制和悲观并发控制等内容。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 通过使用Sqoop导入工具,可以精确控制并高效地将表数据的特定子集导入到HDFS中。具体而言,可以通过在导入命令中添加WHERE子句来指定所需的数据范围,从而在数据库服务器上执行相应的SQL查询,并将查询结果高效地存储到HDFS中。这种方法不仅提高了数据导入的灵活性,还确保了数据的准确性和完整性。 ... [详细]
  • 本文介绍了如何使用Flume从Linux文件系统收集日志并存储到HDFS,然后通过MapReduce清洗数据,使用Hive进行数据分析,并最终通过Sqoop将结果导出到MySQL数据库。 ... [详细]
  • 本文介绍了如何在 Spring 3.0.5 中使用 JdbcTemplate 插入数据并获取 MySQL 表中的自增主键。 ... [详细]
  • 本文介绍了在 MySQL 中如何使用正则表达式来提高查询效率,通过具体示例展示了如何筛选包含中文字符的记录,并详细解释了正则表达式的各种特殊字符和结构。 ... [详细]
  • MicrosoftDeploymentToolkit2010部署培训实验手册V1.0目录实验环境说明3实验环境虚拟机使用信息3注意:4实验手册正文说 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • 从0到1搭建大数据平台
    从0到1搭建大数据平台 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
author-avatar
9baibai_555
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有