在許多情況下,查詢的 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 拒絕,MySQL 會將查詢轉換為沒有外連接作業的查詢。(也就是說,它會將外連接轉換為內部連接。)如果針對該作業產生的任何 NULL
補充列,條件評估結果為 FALSE
或 UNKNOWN
,則稱該條件為外連接作業的 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 拒絕,因為它們對於 NULL
補充列可能為真
T2.B IS NULL
T1.B < 3 OR T2.B IS NOT NULL
T1.B < 3 OR T2.B > 3
檢查條件是否被外連接作業 Null 拒絕的一般規則很簡單
它的形式為
A IS NOT NULL
,其中A
是任何內部資料表的屬性它是一個述詞,其中包含對內部資料表的參考,當其中一個引數為
NULL
時,該述詞的評估結果為UNKNOWN
它是包含 Null 拒絕條件作為連詞的合取
它是 Null 拒絕條件的分離
一個條件在查詢中的一個外連接作業中可能被 Null 拒絕,而在另一個作業中則不會被 Null 拒絕。在此查詢中,WHERE
條件針對第二個外連接作業為 Null 拒絕,但針對第一個外連接作業則不會
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 拒絕,則外連接作業會被內部連接作業取代。
例如,在前面的查詢中,第二個外連接為 Null 拒絕,且可以用內部連接取代
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 值拒絕。這會產生一個完全沒有外部聯結的查詢。
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
條件對於嵌入的外部聯結來說不會被 null 值拒絕,但是外層外部聯結的聯結條件 T2.A=T1.A AND T3.C=T1.C
會被 null 值拒絕。
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