外部联接简化
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,T2
,P(T1,T2)
并作为WHERE
条件(或嵌入联接的联接条件,如果有)的联接。
当优化程序评估外部联接操作的计划时,它仅考虑计划,其中对于每个此类操作,在访问内部表之前访问外部表。由于只有这样的计划才能使用嵌套循环算法执行外部联接,因此优化器的选择受到限制。
考虑这种形式的查询,其中R(T2)
大大缩小了table中匹配行的数量T2
:
SELECT * T1LEFT JOIN T2ON P1(T1,T2)WHERE P(T1,T2) AND R(T2)
如果查询以书面形式执行,则优化器别无选择,只能在限制程度T1
更高的表之前访问限制程度较小的表T2
,这可能会产生效率很低的执行计划。
相反,如果WHERE
条件为空,则MySQL将查询转换为无外部联接操作的查询。(也就是说,它将外部联接转换为内部联接。)如果外部条件运算结果等于FALSE
或针对该操作生成的UNKNOWN
任何NULL
互补行,则条件被认为是null拒绝的。
因此,对于此外部联接:
T1LEFT JOIN T2ON 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 T1LEFT JOIN T2ON T2.A=T1.ALEFT JOIN T3ON T3.B=T1.BWHERE T3.C > 0
如果WHERE
查询中的外部联接操作拒绝该条件为空,则将外部联接操作替换为内部联接操作。
例如,在前面的查询中,第二个外部联接被拒绝为空,并且可以由内部联接代替:
SELECT *FROM T1LEFT JOIN T2ON T2.A=T1.AINNER JOIN T3ON T3.B=T1.BWHERE T3.C > 0
对于原始查询,优化器仅评估与单个表访问顺序兼容的计划T1,T2,T3
。对于重写的查询,它还会考虑访问顺序T3,T1,T2
。
一个外部联接操作的转换可能会触发另一个外部联接操作的转换。因此,查询:
SELECT *FROM T1LEFT JOIN T2ON T2.A=T1.ALEFT JOIN T3ON T3.B=T2.BWHERE T3.C > 0
首先转换为查询:
SELECT *FROM T1LEFT JOIN T2ON T2.A=T1.AINNER JOIN T3ON T3.B=T2.BWHERE T3.C > 0
等效于查询:
SELECT *FROM (T1LEFT JOIN T2ON T2.A=T1.A), T3WHERE T3.C > 0 AND T3.B=T2.B
其余的外部联接操作也可以由内部联接代替,因为条件T3.B=T2.B
被拒绝为空。这将导致查询完全没有外部联接:
SELECT *FROM (T1INNER JOIN T2ON T2.A=T1.A), T3WHERE T3.C > 0 AND T3.B=T2.B
有时,优化器成功替换了嵌入的外部联接操作,但是无法转换嵌入的外部联接。以下查询:
SELECT *FROM T1LEFT JOIN (T2LEFT JOIN T3ON T3.B=T2.B)ON T2.A=T1.AWHERE T3.C > 0
转换为:
SELECT *FROM T1LEFT JOIN (T2INNER JOIN T3ON T3.B=T2.B)ON T2.A=T1.AWHERE T3.C > 0
只能将其重写为仍包含嵌入外部联接操作的表单:
SELECT *FROM T1LEFT 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 T1LEFT JOIN (T2LEFT JOIN T3ON T3.B=T2.B)ON T2.A=T1.A AND T3.C=T1.CWHERE T3.D > 0 OR T1.D > 0
因此,查询可以转换为:
SELECT *FROM T1LEFT JOIN (T2, T3)ON T2.A=T1.A AND T3.C=T1.C AND T3.B=T2.BWHERE T3.D > 0 OR T1.D > 0