原文地址:Nested Join Optimization

与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 则不然。

一般来说,在只包含inner join操作的表达式中可以忽略括号,看看以下的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

但是,这两个表达式并不等价。看这个,如果t1,t2和t3有以下的特点:

1,t1 包含(1),(2)两行

2,t2包含(1,101)这一行

3,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,t3和属性t2.b和t3.b上的任何条件P,等效于此表达式:

t1, t2 LEFT JOIN t3 ON P(t2.b,t3.b)

无论什么时候,一个连接表达式中的连接操作的执行顺序并不是从左往右,我们讨论过嵌套查询,考虑以下查询:

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

在第一个查询中,嵌套查询由左链接操作组成。在第二个查询中,它由一个内连接组成。

在第一个查询中,括号可以被省略,连接表达式的语法结构将规定连接操作的执行顺序。对于第二个查询,括号不能省略,尽管这里的连接表达式可以毫无歧义地被解释。在我们的扩展语法中,第二个查询的(t2,t3)中的括号是必需的,尽管理论上可以在没有它们的情况下解析查询:我们仍然会对查询具有明确的语法结构,因为LEFT JOIN和ON起到表达式(t2,t3)的左和右分隔符。

前面的例子展示了以下重要点:

1,对于仅涉及到内连接(非外链接)的join表达式来说,括号可以被移除,join表达从左到右执行。事实上,表可以以任何顺序执行。

2,一般来说,外连接或外连接与内连接混合也不一样。删除括号可能会改变结果。

具有嵌套外连接的查询以与内连接的查询以相同的流水线方式执行。更准确地说,利用嵌套循环连接算法的变体。回想一下嵌套循环连接执行查询的算法(参见第8.2.1.6节“嵌套循环连接算法”)。假设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(t2,t3)是一样的连接条件p(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表示在该表的每一列使用NULL的行。例如,t1 || t2 || NULL表示通过连接行t1和t2的列而构成的行,对于t3的每一列,为NULL。这样的一个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;
}
}
}

通常,对于外部连接操作中第一个内部表的任何嵌套循环,引入在循环之前关闭的标志,并在循环后检查。当外表中的当前行与表示内部操作数的表匹配时,该标志被打开。如果在循环周期结束时,该标志仍​​然处于关闭状态,则不会为外部表的当前行找到匹配项。在这种情况下,该行由内部表的列补充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必须通过外链接循环,因为它使用了outer join。 T2和T3使用了inner join, 所以这个连接必须在内循环中处理。然而,由于是内连接,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的条件引发,则禁止通过主键从同一嵌套连接中的一个内部表访问到另一个内部表

【mysql 优化 4】嵌套连接优化的更多相关文章

  1. MySQL优化二(连接优化和缓存优化)

    body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-top: 10 ...

  2. 百度APP移动端网络深度优化实践分享(二):网络连接优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<二>连接优化>,感谢原作者的无私分享. 一.前言 在<百度APP移动端网 ...

  3. mysql优化之连接优化(open-files-limit与table_open_cache)

    MySQL打开的文件描述符限制 Can't open file: '.\test\mytable.frm' (errno: 24) OS error code : Too many open file ...

  4. mysql优化之连接优化

    Posted by Money Talks on 2012/02/23 | 第一篇 序章第二篇 连接优化第三篇 索引优化第四篇 查询优化第五篇 到实战中去 连接优化 连接优化主要指客户端连接数据库以及 ...

  5. MySql数据库3【优化4】连接设置的优化

    1.wait_timeout / interactive_timeout  连接超时 服务器关闭连接之前等待活动的秒数.MySQL所支持的最大连接数是有限的,因为每个连接的建立都会消耗内存,因此我们希 ...

  6. 【mysql 优化 5】左连接和右连接优化

    原文地址:8.2.1.8 Left Join and Right Join Optimization mysql以下列方式实现一个A left join B 连接条件: 1,表B设置为依赖于表A和A所 ...

  7. MySQL实验 内连接优化order by+limit 以及添加索引再次改进

    MySQL实验 内连接优化order by+limit 以及添加索引再次改进 在进行子查询优化双参数limit时我萌生了测试更加符合实际生产需要的ORDER BY + LIMIT的想法,或许我们也可以 ...

  8. mysql笔记03 查询性能优化

    查询性能优化 1. 为什么查询速度会慢? 1). 如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减 ...

  9. MySQL优化(二):SQL优化

    一.SQL优化 1.优化SQL一般步骤 1.1 查看SQL执行频率 SHOW STATUS LIKE 'Com_%'; Com_select:执行SELECT操作的次数,一次查询累加1.其他类似 以下 ...

随机推荐

  1. 谷歌浏览器Chrome developer tool详细介绍

    http://www.cr173.com/html/19114_4.html 第 4 页 js调试源码控制面板 5 源码控制面板(js调试) Javascript的调试,基本上是通过源码控制面板和命令 ...

  2. POJ 1651 Multiplication Puzzle (区间DP,经典)

    题意: 给出一个序列,共n个正整数,要求将区间[2,n-1]全部删去,只剩下a[1]和a[n],也就是一共需要删除n-2个数字,但是每次只能删除一个数字,且会获得该数字与其旁边两个数字的积的分数,问最 ...

  3. 中国各运营商(电信、联通、移动、铁通)IP地址段

    除此电信.联通.移动.铁通之外还有教育网.科技网.广电.长城.广电…… 表格下载: http://files.cnblogs.com/files/xiaohi/中国IP网段.zip 以上资料参考: h ...

  4. for...in、for...of、forEach()有什么区别

    本文原链接:https://cloud.tencent.com/developer/article/1360074 for of 和 for in 循环 循环遍历数组的时候,你还在用 for 语句走天 ...

  5. python递归与非递归实现斐波那契数列

    1.题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). 递归实现: class Solution(): def Fibnacci(self ...

  6. 【转】Intellij IDEA 提交代码到远程GitHub仓库

    1.文章参考自:http://my.oschina.net/lujianing/blog/180728 2.设置相关绑定 Settings——Version Control——Git——Path to ...

  7. overloading and overriding

    What is the difference between method overloading and method overriding in Java? Differences between ...

  8. js中异步方案比较完整版(callback,promise,generator,async)

    JS 异步已经告一段落了,这里来一波小总结 1. 回调函数(callback) setTimeout(() => { // callback 函数体 }, 1000) 缺点:回调地狱,不能用 t ...

  9. Mybatis中关于OGNL表达式冲突

    注意设计表字段不能用bor  xor  and  band  eq  neq  lt  gt  lte  gte  shl  shr  ushr

  10. Bootstrap历练实例:向列表组添加链接

    向列表组添加链接 通过使用锚标签代替列表项,我们可以向列表组添加链接.我们需要使用 <div> 代替 <ul> 元素.下面的实例演示了这点: <!DOCTYPE html ...