查詢的 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
) 都不可能為 true
T2.B IS NOT NULL
T2.B > 3
T2.C <= T1.C
T2.B < 2 OR T2.C > 1
像這樣的條件不會被 null 拒絕,因為它們對於 NULL
補足列可能為 true
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