最佳化器使用實體化來啟用更有效率的子查詢處理。實體化透過將子查詢結果產生為暫存資料表(通常在記憶體中)來加速查詢執行。當 MySQL 第一次需要子查詢結果時,它會將該結果實體化為暫存資料表。之後任何時間需要結果時,MySQL 會再次參照暫存資料表。最佳化器可以使用雜湊索引為資料表建立索引,以使查找快速且成本低廉。索引包含唯一值,以消除重複項並縮小資料表。
子查詢實體化會盡可能使用記憶體中的暫存資料表,如果資料表變得太大,則會回復為磁碟上的儲存空間。請參閱第 10.4.4 節,「MySQL 中內部暫存資料表的使用」。
如果未使用實體化,最佳化器有時會將非關聯子查詢重寫為關聯子查詢。例如,以下 IN
子查詢是非關聯的 (where_condition
僅涉及來自 t2
的資料行,而不涉及 t1
)
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
最佳化器可能會將其重寫為 EXISTS
關聯子查詢
SELECT * FROM t1
WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);
使用暫存資料表的子查詢實體化避免了此類重寫,並使子查詢能夠僅執行一次,而不是針對外部查詢的每一列執行一次。
為了在 MySQL 中使用子查詢實體化,必須啟用optimizer_switch
系統變數的 materialization
旗標。(請參閱第 10.9.2 節,「可切換的最佳化」。) 啟用materialization
旗標後,實體化適用於出現在任何位置(在選取清單、WHERE
、ON
、GROUP BY
、HAVING
或 ORDER BY
中)的子查詢述詞,對於屬於以下任何用例的述詞
當沒有外部運算式
oe_i
或內部運算式ie_i
可為 Null 時,述詞具有此形式。N
為 1 或更大。(oe_1, oe_2, ..., oe_N) [NOT] IN (SELECT ie_1, i_2, ..., ie_N ...)
當存在單一外部運算式
oe
和內部運算式ie
時,述詞具有此形式。運算式可以為 Null。oe [NOT] IN (SELECT ie ...)
述詞為
IN
或NOT IN
,且UNKNOWN
(NULL
) 的結果與FALSE
的結果具有相同意義。
以下範例說明 UNKNOWN
和 FALSE
述詞評估的等效性需求如何影響是否可以使用子查詢物化。假設 where_condition
僅涉及來自 t2
的資料行,而不涉及 t1
的資料行,因此子查詢是非相關的。
此查詢適用於物化
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
在這裡,IN
述詞傳回 UNKNOWN
或 FALSE
並不重要。無論如何,來自 t1
的列都不會包含在查詢結果中。
不使用子查詢物化的範例是以下查詢,其中 t2.b
是一個可為 Null 的資料行
SELECT * FROM t1
WHERE (t1.a,t1.b) NOT IN (SELECT t2.a,t2.b FROM t2
WHERE where_condition);
以下限制適用於子查詢物化的使用
內部和外部運算式的類型必須相符。例如,如果兩個運算式都是整數或都是十進位,最佳化工具也許可以使用物化,但如果一個運算式是整數,另一個是十進位,則不能使用物化。
內部運算式不能是
BLOB
。
使用 EXPLAIN
查詢可以提供一些關於最佳化工具是否使用子查詢物化的指示
與不使用物化的查詢執行相比,
select_type
可能會從DEPENDENT SUBQUERY
變更為SUBQUERY
。這表示,對於每個外部列都會執行一次的子查詢,物化使子查詢能夠僅執行一次。對於擴充的
EXPLAIN
輸出,後面SHOW WARNINGS
顯示的文字包括materialize
和materialized-subquery
。
如果陳述式未使用 ORDER BY
或 LIMIT
,並且最佳化工具提示或 optimizer_switch
設定允許子查詢物化,則 MySQL 也可以將子查詢物化套用至使用 [NOT] IN
或 [NOT] EXISTS
子查詢述詞的單一資料表 UPDATE
或 DELETE
陳述式。