最佳化工具可以使用兩種策略處理衍生表參考(這也適用於視圖參考和通用表表達式)
將衍生表合併到外部查詢區塊中
將衍生表實體化為內部暫存表
範例 1
SELECT * FROM (SELECT * FROM t1) AS derived_t1;
在合併衍生表 derived_t1
的情況下,該查詢的執行方式類似於
SELECT * FROM t1;
範例 2
SELECT *
FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1
WHERE t1.f1 > 0;
在合併衍生表 derived_t2
的情況下,該查詢的執行方式類似於
SELECT t1.*, t2.f1
FROM t1 JOIN t2 ON t1.f2=t2.f1
WHERE t1.f1 > 0;
在實體化的情況下,derived_t1
和 derived_t2
會在各自的查詢中被視為個別的表格。
最佳化工具以相同的方式處理衍生表、視圖參考和通用表表達式:它盡可能避免不必要的實體化,這使得能夠將條件從外部查詢下推到衍生表,並產生更有效率的執行計畫。(如需範例,請參閱章節 10.2.2.2,「使用實體化最佳化子查詢」。)
如果合併會導致外部查詢區塊參考超過 61 個基礎表格,最佳化工具會選擇實體化。
如果下列條件全部成立,最佳化工具會將衍生表或視圖參考中的 ORDER BY
子句傳播到外部查詢區塊
外部查詢未分組或彙總。
外部查詢未指定
DISTINCT
、HAVING
或ORDER BY
。外部查詢的
FROM
子句中只有這個衍生表或視圖參考作為來源。
否則,最佳化工具會忽略 ORDER BY
子句。
下列方法可用於影響最佳化工具是否嘗試將衍生表、視圖參考和通用表表達式合併到外部查詢區塊中
可以使用
MERGE
和NO_MERGE
最佳化提示。它們的應用前提是沒有其他規則阻止合併。請參閱 第 10.9.3 節「最佳化提示」。類似地,您可以使用
derived_merge
旗標,該旗標位於optimizer_switch
系統變數中。請參閱 第 10.9.2 節「可切換的最佳化」。預設情況下,此旗標已啟用以允許合併。停用此旗標會阻止合併,並避免ER_UPDATE_TABLE_USED
錯誤。derived_merge
旗標也適用於不包含ALGORITHM
子句的視圖。因此,如果對於使用與子查詢等效的表達式的視圖參照發生ER_UPDATE_TABLE_USED
錯誤,則在視圖定義中加入ALGORITHM=TEMPTABLE
可阻止合併,並且其優先順序高於derived_merge
的值。可以在子查詢中使用任何會阻止合併的結構來停用合併,儘管這些結構對實體化的影響不如明確指定。阻止合併的結構對於衍生表格、通用表格運算式和視圖參照是相同的。
如果最佳化工具選擇實體化策略,而非合併衍生表格,它會如下處理查詢
最佳化工具會延遲衍生表格實體化,直到查詢執行期間需要其內容時才進行。這會提高效能,因為延遲實體化可能會導致根本不必執行。考慮一個將衍生表格的結果聯結到另一個表格的查詢:如果最佳化工具先處理另一個表格,並且發現它沒有傳回任何資料列,則不需要進一步執行聯結,並且最佳化工具可以完全跳過實體化衍生表格。
在查詢執行期間,最佳化工具可能會在衍生表格中新增索引,以加速從該表格擷取資料列。
考慮以下 EXPLAIN
陳述式,用於包含衍生表格的 SELECT
查詢
EXPLAIN SELECT * FROM (SELECT * FROM t1) AS derived_t1;
最佳化工具會延遲衍生表格,直到在 SELECT
執行期間需要結果時才執行,以避免實體化衍生表格。在此情況下,不會執行查詢(因為它發生在 EXPLAIN
陳述式中),因此永遠不需要結果。
即使對於執行的查詢,延遲衍生表格實體化也可能使最佳化工具能夠完全避免實體化。發生這種情況時,查詢執行速度會因為不需要執行實體化而更快。考慮以下查詢,它將衍生表格的結果聯結到另一個表格
SELECT *
FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2
ON t1.f2=derived_t2.f1
WHERE t1.f1 > 0;
如果最佳化先處理 t1
並且 WHERE
子句產生空的結果,則聯結必定為空,並且不需要將衍生表格實體化。
對於衍生表格需要實體化的情況,最佳化工具可能會在實體化的表格中新增索引,以加速對它的存取。如果這種索引能夠讓 ref
存取表格,則可以大幅減少查詢執行期間讀取的資料量。考慮以下查詢
SELECT *
FROM t1 JOIN (SELECT DISTINCT f1 FROM t2) AS derived_t2
ON t1.f1=derived_t2.f1;
如果這樣做能夠讓成本最低的執行計畫使用 ref
存取,則最佳化工具會從 derived_t2
的 f1
資料行建立索引。新增索引後,最佳化工具可以將實體化的衍生表格視為具有索引的正規表格,並且同樣會受益於產生的索引。與沒有索引的查詢執行成本相比,建立索引的額外負荷微不足道。如果 ref
存取的成本高於其他某些存取方法,則最佳化工具不會建立索引,也不會有任何損失。
對於最佳化工具追蹤輸出,合併的衍生表格或視圖參照不會顯示為節點。只有其底層表格會顯示在頂層查詢的計畫中。
適用於衍生表格實體化的內容也適用於通用表格運算式 (CTE)。此外,以下考量因素特別適用於 CTE。
如果 CTE 由查詢實體化,則即使查詢多次參照它,該查詢也只會實體化一次。
遞迴 CTE 永遠會被實體化。
如果 CTE 被實體化,則最佳化工具會自動新增相關的索引 (如果它估計索引可以加速頂層陳述式對 CTE 的存取)。這與衍生表格的自動索引類似,只是如果多次參照 CTE,則最佳化工具可能會建立多個索引,以便以最合適的方式加速每個參照的存取。
最佳化工具提示 MERGE
和 NO_MERGE
可以套用到 CTE。頂層陳述式中的每個 CTE 參照都可以有自己的提示,允許選擇性地合併或實體化 CTE 參照。以下陳述式使用提示來指示應合併 cte1
,並且應實體化 cte2
。
WITH
cte1 AS (SELECT a, b FROM table1),
cte2 AS (SELECT c, d FROM table2)
SELECT /*+ MERGE(cte1) NO_MERGE(cte2) */ cte1.b, cte2.d
FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;
針對 CREATE VIEW
的 ALGORITHM
子句不會影響任何 WITH
子句的實體化,該子句位於視圖定義中的 SELECT
陳述式之前。請考慮以下陳述式
CREATE ALGORITHM={TEMPTABLE|MERGE} VIEW v1 AS WITH ... SELECT ...
ALGORITHM
值只會影響 SELECT
的實體化,而不是 WITH
子句。
如先前所述,如果實體化 CTE,則會實體化一次,即使被多次參照也是如此。為了指出一次性實體化,最佳化工具追蹤輸出會包含 creating_tmp_table
的實例,以及一或多個 reusing_tmp_table
的實例。
CTE 類似於衍生表格,其中 materialized_from_subquery
節點會遵循參照。對於被多次參照的 CTE 也是如此,因此不會重複 materialized_from_subquery
節點 (這會讓人以為子查詢被執行多次,並產生不必要的詳細輸出)。只有一個 CTE 參照具有完整的 materialized_from_subquery
節點,其中包含其子查詢計畫的描述。其他參照則具有精簡的 materialized_from_subquery
節點。相同的想法也適用於 TRADITIONAL
格式的 EXPLAIN
輸出:不顯示其他參照的子查詢。