2013-02-13 21 views
4

我有一個簡單的日期表(Date,DateID),其中包含1900年1月1日至2100年12月31日之間的日期列表。「Between」運算符在使用參數時生成錯誤的查詢計劃

使用between操作者和硬編碼的參數值從表中選擇時,獲得帶有3個估計行正確的查詢計劃相比2個實際行:

select v.Date from Dates v 
where v.Date between '20130128' and '20130129'; 

然而當更換硬編碼與參數的值,查詢計劃更改爲一個非常貧窮的計劃,超過6000估計行只有2實際行:

select v.Date from Dates v 
where v.Date between @startdate and @enddate; 

查詢計劃本身是相同的,只是在估計行時,造成的差異參數化查詢的運行速度比硬編碼查詢慢大約4倍。有什麼我失蹤了,爲什麼參數化版本運行速度如此之慢,以及我可以給SQL Server哪些索引/提示以幫助它使用正確的查詢計劃?

一些額外的信息:

  • 使用簡單的平等=標準時,就不會出現這個問題,似乎特定於between運營商。
  • 如果我將option(recompile)添加到參數化查詢的末尾,我會得到一個完美的查詢計劃,與硬編碼查詢相同。
  • 日期表只有兩列Date和DateID,在主鍵DateID列上有聚簇索引,在Date列上有唯一的非聚簇索引。所有更新的統計數據。
  • 查詢計劃爲硬編碼查詢執行自動參數化,用@ 1和@ 2替換硬編碼的值,並將查詢顯示爲大寫。它似乎沒有爲參數化查詢執行任何轉換。
  • 使用SQL Server 2008 R2。

我知道足夠 實現 嫌疑這是某種參數嗅探問題。爲什麼不將option(recompile)添加到查詢中?這被用作更大複雜查詢的一部分,我理解的最佳做法是讓SQL Server儘可能地從緩存中重新使用查詢計劃。

編輯和更新:感謝迄今爲止的深思熟慮的回覆。爲了進一步細化問題,查詢計劃對上述兩個查詢都使用了一個非常好的索引,但爲什麼它沒有認識到日期範圍對於參數化查詢只有兩天寬,爲什麼它卻認爲範圍是6000行寬?尤其是當查看查詢計劃時,SQL Server正在爲硬編碼查詢執行自動參數化?在底層查詢計劃中,兩個計劃看起來都是相同的,因爲它們都是參數化的!

+0

將'vdate在@startdate和@ enddate'之間替換爲'v.Date> = @startdate AND v.Date <= @ enddate'時會發生什麼? – 2013-02-13 10:26:10

+0

由於丹尼爾 - 測試和語義相同,生成的查詢計劃中沒有任何更改,仍然生成約6000估計行。 – Tamar 2013-02-13 10:30:30

回答

3

查詢計劃基於首次運行查詢時的參數值。這叫做parameter sniffing。當您添加option (recompile)時,會爲每次執行生成一個新計劃。

查詢計劃基於SQL查詢的散列進行高速緩存。所以這兩個版本的查詢都有不同的緩存槽。

添加option (recompile)是一個很好的解決方案。你也可以使用:

option (optimize for (@startdate = '20130128', @enddate = '20130129')); 

生成查詢計劃,如果這些值已在已經通過

爲了進行測試,可以從緩存中刪除了所有的計劃。

DBCC FREEPROCCACHE 
+0

感謝Andomar,我寧願避免'選擇(重新編譯)',因爲這個查詢正在用於一個更大的複雜查詢中,並且不希望整個查詢每次都要重新編譯。 'DBCC FREEPROCCACHE'對生成的查詢計劃沒有影響。 – Tamar 2013-02-13 11:19:54

+0

我曾嘗試過針對您的建議進行優化,如果日期相隔一天,它會生成正確的查詢計劃,但如果日期範圍較寬(如一個月),則不會生成正確的計劃。 – Tamar 2013-02-13 11:31:26

+0

感謝@Andomar,標記爲「DBCC FREEPROCCACHE」沒有改進日期子查詢的查詢計劃,當然,如果用作更大複雜查詢的一部分,它確實可以提高查詢計劃的估計值,因此解決了問題。 – Tamar 2013-02-13 23:14:12

2

問題可能是由於parameter sniffing,這是一種基於您首次通過的參數優化查詢計劃的技術。

我在參數嗅探過去使用過日期參數時遇到了不好的經歷 - 參數嗅探顯然有一個機會,即最初爲查詢輸入的值並不代表該查詢的典型用法(導致查詢計劃針對非典型值進行了優化),但是使用日期參數特別是我經常發現生成的查詢計劃對於提供的所有值實際上效率非常低。我不知道爲什麼這是禁用參數嗅探通常修復它。

您可以通過使用OPTIMIZE FOR UNKNOWN(我從來沒有使用過,因此不能保證它可以正常工作)防止SQL Server參數嗅探,或者將參數複製到本地變量中似乎可以防止SQL Server執行參數嗅探

-- Perform the query using @StartDateLocal and @EndDateLocal 
DECLARE @StartDateLocal DATETIME; 
SET @StartDateLocal = @StartDate; 

禁用參數以這種方式嗅探是不是每個相比,執行查詢,除非查詢是相當緩慢的成本編譯查詢計劃通常是比較昂貴的時間迫使查詢計劃的重新編譯更好。

+0

感謝賈斯汀,之前曾嘗試過「優化未知」,但它對查詢計劃沒有影響。我很高興嘗試使用局部變量,但在StackOverflow上閱讀了很多關於這種做法的評論? – Tamar 2013-02-13 11:14:55

+0

@Tamar任何參考?我可以想象避免它的一個很好的理由是因爲它阻止了參數嗅探,但是因爲這裏的意圖可能不是問題:)我想不出其他原因來避免它,但它並不意味着沒有一個。 – Justin 2013-02-13 11:33:11

+0

請參閱[此鏈接](http://stackoverflow.com/questions/14468603/does-assigning-stored-procedure-input-parameters-to-local-variables-help-optimiz)等等。一致認爲,防止參數嗅探會有所幫助,但需要以一種不會在更大的查詢中使用時不會破壞更廣泛的計劃優化的方式來實現。 – Tamar 2013-02-13 11:44:42

相關問題