Sql-Server-2008

當從帶有外部參數化“where”子句的視圖呼叫時,視窗函式會導致糟糕的執行計劃

  • June 19, 2020

我很久以前就遇到過這個問題,我找到了一個適合我的解決方法並忘記了它。

但是現在有關於 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 行。
  • 巨大的加入。
  • 計算所有partitions 的視窗(大約 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 中。

一些討論它的文章是

直到 2012 年(包括 2012 年)的所有目前版本的 SQL Server 都無法將分區組上的過濾器推送到參數化謂詞的序列項目之外,除非option(recompile)使用了(如果 2008+)。

提示的替代方法recompile是重寫查詢以使用@a1ex07建議的參數化內聯 TVF )

我會嘗試用表值 udf 替換視圖。這樣它將首先過濾記錄,然後應用視窗函式。此函式可以接受表參數,因此您可以將多個傳遞order_number給它

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