最佳化器使用實體化來啟用更有效率的子查詢處理。實體化透過將子查詢結果產生為暫存資料表(通常在記憶體中)來加速查詢執行。當 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 設定允許子查詢實體化。