最佳化工具可以使用兩種策略處理衍生表參考(這也適用於視圖參考和通用表表達式)
將衍生表合併到外部查詢區塊中
將衍生表實體化為內部暫存資料表
範例 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,「最佳化工具提示」。同樣地,您可以使用
optimizer_switch
系統變數的derived_merge
旗標。請參閱章節 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
子句不會影響任何在視圖定義中 SELECT
陳述式之前的 WITH
子句的實體化。考慮這個陳述式:
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
輸出:其他參考的子查詢不會顯示。