查询的 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)
的内部连接表达式都将被列表 T1,T2
替换,P(T1,T2)
被作为连接到 WHERE
条件(如果有嵌入连接,则连接到嵌入连接的连接条件)的合取词。
当优化器评估外连接操作的计划时,它只考虑以下计划:对于每个外连接操作,外表在内表之前访问。优化器选择有限,因为只有此类计划才可以使用嵌套循环算法执行外连接。
考虑一个这种形式的查询,其中 R(T2)
大大缩小了来自表 T2
的匹配行数
SELECT * T1 FROM T1
LEFT JOIN T2 ON P1(T1,T2)
WHERE P(T1,T2) AND R(T2)
如果查询按照原样执行,优化器别无选择,只能在更严格的表 T2
之前访问限制较少的表 T1
,这可能会产生非常低效的执行计划。
相反,如果 WHERE
条件为 null-rejected,MySQL 会将查询转换为不包含外连接操作的查询。(也就是说,它将外连接转换为内部连接。)如果条件对于外连接操作的任何 NULL
-complemented 行都计算为 FALSE
或 UNKNOWN
,则该条件被认为是 null-rejected。(对于操作,T2
列设置为 NULL
)。
因此,对于此外连接
T1 LEFT JOIN T2 ON T1.A=T2.A
以下条件是 null-rejected,因为它们不可能在任何 NULL
-complemented 行(T2
列设置为 NULL
)上为真
T2.B IS NOT NULL
T2.B > 3
T2.C <= T1.C
T2.B < 2 OR T2.C > 1
以下条件不是 null-rejected,因为它们可能在 NULL
-complemented 行上为真
T2.B IS NULL
T1.B < 3 OR T2.B IS NOT NULL
T1.B < 3 OR T2.B > 3
检查条件是否为外连接操作的 null-rejected 的一般规则很简单
它是
A IS NOT NULL
形式,其中A
是任何内表的属性它是一个谓词,包含对内表的引用,当其中一个参数为
NULL
时计算为UNKNOWN
它是一个合取词,包含一个作为合取词的 null-rejected 条件
它是 null-rejected 条件的析取
一个条件可以对于查询中的一个外连接操作为 null-rejected,而对于另一个外连接操作则不为 null-rejected。在此查询中,WHERE
条件对于第二个外连接操作为 null-rejected,但对于第一个外连接操作则不为 null-rejected
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
LEFT JOIN T3 ON T3.B=T1.B
WHERE T3.C > 0
如果 WHERE
条件对于查询中的外连接操作为 null-rejected,则外连接操作将被替换为内部连接操作。
例如,在前面的查询中,第二个外连接为 null-rejected,可以替换为内部连接
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
为 null-rejected。这导致了完全不包含外连接的查询
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