Sql-Server

當建構端為空時,SQL Server 為什麼/何時評估內部散列連接的探測端?

  • December 6, 2019

設置

DROP TABLE IF EXISTS #EmptyTable, #BigTable

CREATE TABLE #EmptyTable(A int);
CREATE TABLE #BigTable(A int);

INSERT INTO #BigTable
SELECT TOP 10000000 CRYPT_GEN_RANDOM(3)
FROM   sys.all_objects o1,
      sys.all_objects o2,
      sys.all_objects o3;

詢問

WITH agg
    AS (SELECT DISTINCT a
        FROM   #BigTable)
SELECT *
FROM   #EmptyTable E
      INNER HASH JOIN agg B
                   ON B.A = E.A;

執行計劃

在此處輸入圖像描述

問題

這是我今天之前沒有註意到的現象的簡化再現。我對內部雜湊連接的期望是,如果建構輸入為空,則不應執行探測端,因為連接不能返回任何行。上面的範例與此相矛盾,並從表中讀取了 1000 萬行。這使查詢的執行時間增加了 2.196 秒(99.9%)。

額外的觀察

  1. OPTION (MAXDOP 1)執行計劃不會從#BigTable. ActualExecutions適用0於散列連接內部的所有運算符。
  2. 對於查詢SELECT * FROM #EmptyTable E INNER HASH JOIN #BigTable B ON B.A = E.A- 我得到了一個並行計劃,散列連接內部的掃描運算符確實具有ActualExecutionsDOP,但仍然沒有讀取任何行。該計劃沒有重新分區流運算符(或聚合)

問題

這裡發生了什麼?為什麼原計劃會出現問題,而其他情況卻沒有?

當建構為空時不執行連接的探測端是一種優化。當探測端有子分支時,即存在交換運算符時,它不適用於並行行模式雜湊連接。

許多年前,Adam Machanic 在現已解散的 Connect 回饋網站上發表了類似的報告。該場景是探針端的一個啟動過濾器,它意外地執行了它的子操作符。微軟的回答是,引擎需要保證某些結構已初始化,而唯一明智的強制執行方法是確保打開探測端操作符。

我自己對細節的回憶是,不初始化子樹會導致難以修復的並行時序錯誤。確保子分支啟動是解決這些問題的方法。

批處理模式雜湊聯接沒有這種副作用,因為管理執行緒的方式不同。

在您的特定情況下,效果更明顯,因為雜湊聚合是阻塞的;它在迭代器的 Open() 呼叫期間消耗其全部輸入。當探測端只有流式操作符時,性能影響通常會更有限,這取決於將第一行返回到雜湊連接的探測端需要多少工作。

引用自:https://dba.stackexchange.com/questions/254947