Sql-Server-2008
當從帶有外部參數化“where”子句的視圖呼叫時,視窗函式會導致糟糕的執行計劃
我很久以前就遇到過這個問題,我找到了一個適合我的解決方法並忘記了它。
但是現在有關於 SO 的問題,所以我願意提出這個問題。
有一個視圖以非常直接的方式(訂單+訂單行)連接幾個表。
當不帶
where
子句查詢時,視圖返回幾百萬行。然而,從來沒有人這樣稱呼它。通常的查詢是
select * from that_nasty_view where order_number = 123456;
這將返回 5m 中的大約 10 條記錄。
一件重要的事情:視圖包含一個視窗函式 ,
rank()
它由始終用於查詢視圖的欄位精確劃分:rank() over (partition by order_number order by detail_line_number)
現在,如果使用查詢字元串中的文字參數查詢此視圖,如上所示,它會立即返回行。執行計劃很好:
- 使用索引在兩個表上查找索引
order_number
(返回 10 行)。- 在返回的微小結果上計算視窗。
- 選擇。
但是,當以參數化方式呼叫視圖時,事情會變得很糟糕:
Index scan
在所有表上忽略索引。返回 5m 行。- 巨大的加入。
- 計算所有
partition
s 的視窗(大約 500k 個視窗)。Filter
從 5m 中取出 10 行。- 選擇
在涉及參數的所有情況下都會發生這種情況。它可以是 SSMS:
declare @order_number int = 123456; select * from that_nasty_view where order_number = @order_number;
它可以是 ODBC 客戶端,例如 Excel:
select * from that_nasty_view where order_number = ?
或者它可以是使用參數而不是 sql 連接的任何其他客戶端。
如果視窗函式從視圖中移除,它執行得非常快,不管它是否帶有參數查詢。
我的解決方法是刪除有問題的功能並在稍後階段重新應用它。
但是,什麼給了?SQL Server 2008 處理視窗函式的方式真的是一個錯誤嗎?
這似乎是一個長期存在的問題,以一種或另一種形式不斷出現,並且仍然存在於 SQL Server 2012 中。
一些討論它的文章是
- 性能調整 101 - 2005
- 使用序列投影計劃回歸 - 2008 R2 @Paul White
- 公用表表達式 (CTE)、視窗函式和視圖 - 2012 @Jonathan Kehayias
- 視窗函式和視圖的問題
直到 2012 年(包括 2012 年)的所有目前版本的 SQL Server 都無法將分區組上的過濾器推送到參數化謂詞的序列項目之外,除非
option(recompile)
使用了(如果 2008+)。提示的替代方法
recompile
是重寫查詢以使用@a1ex07建議的參數化內聯 TVF )
我會嘗試用表值 udf 替換視圖。這樣它將首先過濾記錄,然後應用視窗函式。此函式可以接受表參數,因此您可以將多個傳遞
order_number
給它