【MySQL系列--優化2】——外連線簡化
許多情況下簡化了查詢的FROM子句中的table 表示式。 在解析器階段,具有右外連線操作的查詢將轉換為僅包含左連線操作的等效查詢。在一般情況下,執行轉換,使得該許可權加入:
(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條件(或連線到嵌入join,如果有的話)。
當優化器評估外部聯接操作的計劃時,僅考慮在每個此類操作之前,在內部表之前訪問外部表的計劃。優化器的選擇是有限的,因為只有這樣的計劃才能使用巢狀迴圈演算法來執行外連線。
考慮這種形式的查詢,其中R(T2)大大縮小了表T2中匹配行的數量:
SELECT * T1 LEFT JOIN T2 ON P1(T1,T2)
WHERE P(T1,T2) AND R(T2)
如果以書面形式執行查詢,則優化程式別無選擇,只能在更受限制的表T2之前訪問較不受限制的表T1,這可能會產生非常低效的執行計劃。
相反,如果WHERE條件為空拒絕,MySQL將查詢轉換為無外部連線操作的查詢。 (即,它將外部連線轉換為內部連線。)對於外部連線操作,如果為操作生成的任何NULL補碼行計算為FALSE或UNKNOWN,則說明條件被拒絕。
從而,對於這個外連線:
T1 LEFT JOIN T2 ON T1.A=T2.A
這些條件因為對於任何NULL互補行(T2列設定為NULL)不能為true而被空拒絕:
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是任何內部表的屬性
- 它是一個包含對內部表的引用的謂詞,當其中一個引數為NULL時
- 它將評估為UNKNOWN
- 它是一個包含無效拒絕條件作為結合的連線 這是無效拒絕條件的分離
對於查詢中的一個外部連線操作,條件可以被空拒絕,而對另一個外部聯接操作不能被空拒絕。在此查詢中,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和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