Sql-Server

恆定掃描加入

  • November 1, 2019

在準備我之前的恆定掃描問題時,我VALUES以各種方式進行了試驗,並遇到了關於加入的事情,VALUES這對我來說看起來很奇怪。

設置很簡單

CREATE TABLE #data ([Id] int);
INSERT INTO #data VALUES (101), (103);

然後有一個查詢

DECLARE @id1 int = 101, @id2 int = 102;

SELECT *
FROM (VALUES (@id1), (@id2)) p([Id])
   FULL HASH JOIN #data d ON d.[Id] = p.[Id];

它沒有什麼特別之處。如果你執行它,它會工作並產生結果。這是它的執行計劃

恆定掃描加入計劃

VALUES但是從中刪除一行

SELECT *
FROM (VALUES (@id1)) p([Id])
   FULL HASH JOIN #data d ON d.[Id] = p.[Id];

導致優化器失敗

消息 8622,級別 16,狀態 1,第 1 行

查詢處理器無法生成查詢計劃…

為什麼?有沒有辦法(除了將參數放入臨時表)使用雜湊算法使其工作?

**注意:**這不是真正的設備,用於研究優化器的行為和能力。


上面的例子在

Microsoft SQL Server 2017 (RTM-CU15-GDR) (KB4505225) - 14.0.3192.2 (X64)

為什麼?

簡而言之。因為HASH提示射擊優化器的腿和優化器本身射擊到另一個。被兩個優化器擊中不能越過終點線。


為了更好地說明發生了什麼,讓我們重寫有問題的查詢以連接兩個VALUES並改用合併算法

DECLARE @id1 int = 101, @id3 int = 103;

SELECT *
FROM (VALUES (@id1)) p([Id])
   FULL MERGE JOIN (VALUES (@id1), (@id3)) d([Id]) ON d.[Id] = p.[Id];

這個查詢的執行計劃很簡單。有兩個恆定掃描輸入的合併連接運算符。

執行計劃

這兩個常量掃描與優化器不同。

執行計劃屬性

一個表示單行輸入的列名以 為前綴Expr,而另一個表示多行輸入的列名以 為前綴Union。在 Merge Join 謂詞中訪問來自多行常量掃描的數據是一種“按引用[Union1001]” (@id1``[Expr1000]

這種“按引用”→“按值”替換是在早期優化階段執行的標量映射的結果。

可以看到(使用跟踪標誌 8606)在輸入樹連接謂詞中是[Union1001] = [Expr1000]

*** 輸入樹:***
...
LogOp_FullOuterJoin
...
ScaOp_Comp x_cmpEq
ScaOp_Identifier COL:Union1001
           **ScaOp_Identifier COL:Expr1000**
...

但是在簡化樹中它變成了[Union1001] = @id1

*** 簡化樹:***
LogOp_FullOuterJoin
...
ScaOp_Comp x_cmpEq
ScaOp_Identifier COL:Union1001
           **ScaOp_Identifier COL:@id1**

標量映射是投影拉動邏輯的一部分,實際上是在進入簡化階段之前執行的。

之前可能確實注意到,Merge Join 節點只有殘差謂詞,沒有連接相等謂詞。這是因為連接相等謂詞已被標量映射消除。is 相等謂詞,[Union1001] = @id1但不能用作連接相等謂詞。為此,它必須從兩個輸入中引用列,但@id1它是可變的而不是列。

因此,ON d.[Id] = p.[Id]最初是 equijoin,查詢轉換為 non-equijoin(這是特殊情況,因此,順便說一下,優化器沒有在 Merge Join 下面為未排序的常量掃描輸入引入排序)。幸運的是,在合併算法的情況下,優化器有這樣的非等連接替代方案。

在使用雜湊算法的情況下,不存在非等連接替代方案,因此連接等式謂詞消除會導致優化器稍後失敗。


有沒有辦法(除了將參數放入臨時表)使用雜湊算法使其工作?

沒有阻止標量映射的跟踪標誌(*),查詢槓桿、會話級別和啟動都沒有。並且沒有可以關閉的優化規則來防止它,因為它不是由規則執行的。

我只能通過在COptExpr::PexprMapScalar常式中設置斷點來執行有問題的查詢

在此處輸入圖像描述

eax並在呼叫後修改寄存器的值ScaOp_Identifier::ClassNo以使SQL Server認為第二個操作數ScaOp_Comp不是標識符。

這是問題中發布的問題查詢的簡化樹

*** 簡化樹:***
LogOp_FullOuterJoin
LogOp_ConstTableGet (1) COL: Expr1000
ScaOp_Identifier COL:@id1
LogOp_Get TBL:#data(別名 TBL:d)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL:[d].Id
           **ScaOp_Identifier COL:Expr1000**
*******************

這是它的計劃

它實際上沒有什麼意義,因為獲得的計劃成本是 0.0210675 個單位,而在沒有HASH提示的情況下執行查詢會導致執行計劃與 Merge Join (注意沒有再次在 Merge Join 下面排序)

執行計劃無提示

成本 0.0088948 單位。


(*)可能存在跟踪標誌的組合。我認為不是,但我沒有探索所有程式碼路徑,所以我不確定。

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