• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 外部联接简化

    FROM在许多情况下,查询子句中的表表达式都得到了简化。

    在解析器阶段,具有右外部联接操作的查询将转换为仅包含左联接操作的等效查询。在一般情况下,执行转换时要进行以下右连接:

    (T1, ...) RIGHT JOIN (T2, ...) ON P(T1, ..., T2, ...)
    

    成为以下等效的左联接:

    (T2, ...) LEFT JOIN (T1, ...) ON P(T1, ..., T2, ...)
    

    表单的所有内部联接表达式T1 INNER JOIN T2 ON P(T1,T2)都由list替换T1,T2P(T1,T2)并作为WHERE条件(或嵌入联接的联接条件,如果有)的联接。

    当优化程序评估外部联接操作的计划时,它仅考虑计划,其中对于每个此类操作,在访问内部表之前访问外部表。由于只有这样的计划才能使用嵌套循环算法执行外部联接,因此优化器的选择受到限制。

    考虑这种形式的查询,其中R(T2)大大缩小了table中匹配行的数量T2

    SELECT * T1 LEFT JOIN T2 ON P1(T1,T2)
      WHERE P(T1,T2) AND R(T2)
    

    如果查询以书面形式执行,则优化器别无选择,只能在限制程度T1更高的表之前访问限制程度较小的表T2,这可能会产生效率很低的执行计划。

    相反,如果WHERE条件为空,则MySQL将查询转换为无外部联接操作的查询。(也就是说,它将外部联接转换为内部联接。)如果外部条件运算结果等于FALSE或针对该操作生成的UNKNOWN任何NULL互补行,则条件被认为是null拒绝的。

    因此,对于此外部联接:

    T1 LEFT JOIN T2 ON T1.A=T2.A
    

    诸如此类的条件将被拒绝为null,因为它们对于任何NULL补行(T2列设置为NULL)都无法成立:

    T2.B IS NOT NULL
    T2.B > 3
    T2.C <= T1.C
    T2.B < 2 OR T2.C > 1
    

    诸如此类的条件不能为空,因为它们对于NULL-补行可能是正确的:

    T2.B IS NULL
    T1.B < 3 OR T2.B IS NOT NULL
    T1.B < 3 OR T2.B > 3
    

    检查外部联接操作的条件是否为空的通用规则很简单:

    • 它的形式为A IS NOT NULL,其中A任何内部表的属性是
    • 这是一个谓词,包含对内部表的引用,该内部表的求值是UNKNOWN何时其参数之一为NULL
    • 它是一个包含空值拒绝条件作为合取词的连词
    • 它是零值拒绝条件的析取

    对于查询中的一个外部联接操作,条件可以为null拒绝,而对另一个条件则不能为null。在此查询中,WHERE第二个外部联接操作的条件为空,但第一个条件的条件为空:

    SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                     LEFT JOIN T3 ON T3.B=T1.B
      WHERE T3.C > 0
    

    如果WHERE查询中的外部联接操作拒绝该条件为空,则将外部联接操作替换为内部联接操作。

    例如,在前面的查询中,第二个外部联接被拒绝为空,并且可以由内部联接代替:

    SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                     INNER JOIN T3 ON T3.B=T1.B
      WHERE T3.C > 0
    

    对于原始查询,优化器仅评估与单个表访问顺序兼容的计划T1,T2,T3。对于重写的查询,它还会考虑访问顺序T3,T1,T2

    一个外部联接操作的转换可能会触发另一个外部联接操作的转换。因此,查询:

    SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                     LEFT JOIN T3 ON T3.B=T2.B
      WHERE T3.C > 0
    

    首先转换为查询:

    SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                     INNER JOIN T3 ON T3.B=T2.B
      WHERE T3.C > 0
    

    等效于查询:

    SELECT * FROM (T1 LEFT JOIN T2 ON T2.A=T1.A), T3
      WHERE T3.C > 0 AND T3.B=T2.B
    

    其余的外部联接操作也可以由内部联接代替,因为条件T3.B=T2.B被拒绝为空。这将导致查询完全没有外部联接:

    SELECT * FROM (T1 INNER JOIN T2 ON T2.A=T1.A), T3
      WHERE T3.C > 0 AND T3.B=T2.B
    

    有时,优化器成功替换了嵌入的外部联接操作,但是无法转换嵌入的外部联接。以下查询:

    SELECT * FROM T1 LEFT JOIN
                  (T2 LEFT JOIN T3 ON T3.B=T2.B)
                  ON T2.A=T1.A
      WHERE T3.C > 0
    

    转换为:

    SELECT * FROM T1 LEFT JOIN
                  (T2 INNER JOIN T3 ON T3.B=T2.B)
                  ON T2.A=T1.A
      WHERE T3.C > 0
    

    只能将其重写为仍包含嵌入外部联接操作的表单:

    SELECT * FROM T1 LEFT JOIN
                  (T2,T3)
                  ON (T2.A=T1.A AND T3.B=T2.B)
      WHERE T3.C > 0
    

    在查询中转换嵌入式外部联接操作的任何尝试都必须考虑将外部联接与WHERE条件一起嵌入的联接条件。在此查询中,WHERE嵌入的外部联接的条件不为空,但嵌入外部联接的联接条件T2.A=T1.A AND T3.C=T1.C为空:

    SELECT * FROM T1 LEFT JOIN
                  (T2 LEFT JOIN T3 ON T3.B=T2.B)
                  ON T2.A=T1.A AND T3.C=T1.C
      WHERE T3.D > 0 OR T1.D > 0
    

    因此,查询可以转换为:

    SELECT * FROM T1 LEFT JOIN
                  (T2, T3)
                  ON T2.A=T1.A AND T3.C=T1.C AND T3.B=T2.B
    WHERE T3.D > 0 OR T1.D > 0