最佳化器使用實體化來啟用更有效率的子查詢處理。實體化透過將子查詢結果產生為暫存資料表(通常在記憶體中)來加速查詢執行。當 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
旗標後,實體化適用於任何位置(在 select 清單、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
是一個可為空的欄位。
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
。
MySQL 也可以將子查詢實體化應用於使用 [NOT] IN
或 [NOT] EXISTS
子查詢述詞的單表 UPDATE
或 DELETE
陳述式,前提是該陳述式不使用 ORDER BY
或 LIMIT
,並且最佳化工具提示或 optimizer_switch
設定允許子查詢實體化。