如何讓交叉應用在視圖上逐行操作?
我們有一個針對單項查詢優化的視圖(200ms 無並行):
select * from OptimizedForSingleObjectIdView e2i where ObjectId = 3374700
它也適用於小組靜態 id (~5)。
select * from OptimizedForSingleObjectIdView e2i where ObjectId in (3374700, 3374710, 3374720, 3374730, 3374740);
但是,如果對象來自外部源,那麼它會生成一個緩慢的計劃。執行計劃顯示視圖部分的執行分支忽略了 ObjectId 上的謂詞,而在原始情況下,它使用它們來執行索引查找。
select v.* from ( select top 1 ObjectId from Objects where ObjectId % 10 = 0 order by ObjectId ) o join OptimizedForSingleObjectIdView v -- (also tried inner loop join) on v.ObjectId = o.ObjectId;
我們不希望投資於“雙重”優化非特殊情況的視圖。相反,我們“尋求”的解決方案是對每個對象重複呼叫一次視圖,而不求助於 SP。
大多數情況下,以下解決方案逐行呼叫視圖。但是這次不是,甚至不是只有 1 個對象:
select v.* from ( select top 1 ObjectId from Objects where ObjectId % 10 = 0 -- non-trivial predicate order by ObjectId ) o cross apply ( select top 2000000000 * from OptimizedForSingleObjectIdView v_ where ObjectId = o.ObjectId order by v_.SomeField ) v;
有一次我認為有人聲稱在呼叫 UDF 時可以保證交叉應用可以逐行執行,但這也失敗了:
create function FunctionCallingView(@pObjectId bigint) returns table as return select * from OptimizedForSingleObjectIdView where ObjectId = @pObjectId; select v.* from ( select top 1 ObjectId from Objects where ObjectId % 10 = 0 order by ObjectId ) o cross apply FunctionCallingView(o.ObjectId) v
添加選項(強制順序)沒有幫助 - 但是視圖中已經有兩個雜湊提示。暫時移除它們並沒有幫助並減慢單個案例的速度。
這是基於函式的慢速案例的估計計劃片段。1行的估計是正確的。最右邊(未顯示)是一個不包括前 1 個結果的搜尋謂詞。這似乎與我們遇到的其他情況相似,在這些情況下,表搜尋中的單數探測值不用作其他地方的搜尋謂詞。
如果不使用引入新的 T-SQL 執行範圍的東西,例如非內聯(多語句,)表值函式,就不可能完全保證評估外部查詢的每行視圖。
BEGIN...END
這幾乎是針對您之前的問題如何使用合併提示來隔離 SQL Server 中的複雜查詢而給出的建議。有一次我認為有一種說法是,當它呼叫 UDF 時,可以保證逐行執行交叉應用
這不適用於內聯表值函式,因為在優化開始之前定義已擴展為呼叫查詢。
也就是說,您可以做一些事情來強烈鼓勵預期的結果。
執行計劃顯示視圖部分的執行分支忽略了謂詞,
ObjectId
而在原始情況下它使用它們來執行索引查找。您期望
ObjectId
使用每個行車行的索引搜尋“在視圖內”評估這些值。這是相關的嵌套循環加入(應用)的執行方式。請注意,使用APPLY
T-SQL 語言元素並不能保證物理執行將使用應用樣式。聽起來好像 SQL Server 選擇使用在Nested Loops Join
ObjectId
運算符處測試的值來執行。這是一種不相關或幼稚的嵌套循環連接執行模式。這很可能是由您在視圖中使用的連接提示引起的。通常要避免連接提示,因為它們極大地限制了優化器的自由度,而不僅僅是連接的物理類型。特別是,連接提示還強制整個查詢的連接順序(就像您使用
FORCE ORDER
提示一樣)並阻止與聚合放置和策略相關的一些優化,以及除此之外的許多其他方面。如果您確實必須在視圖中包含連接提示(我強烈建議您通常避免這種情況),您可能會發現獲得所需計劃形狀的最可靠方法是:
- 使用視圖定義(不引用視圖)創建一個內聯(
RETURNS TABLE
) 函式。@ObjectId
作為函式的參數提供。- 使用函式體中的參數放置謂詞,以使索引查找更有可能。
- 如果確實每次使用都會導致查找,則可以使用函式內部的表格
FORCESEEK
提示。- 使用 呼叫新的內聯函式
APPLY
。我通常不喜歡使用特定的語法和提示來試圖強制特定的物理計劃形狀。通過參數化查詢和使用計劃指南保證計劃形狀,您可能會獲得更普遍的成功。