我有一個簡單的日期表(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正在爲硬編碼查詢執行自動參數化?在底層查詢計劃中,兩個計劃看起來都是相同的,因爲它們都是參數化的!
將'vdate在@startdate和@ enddate'之間替換爲'v.Date> = @startdate AND v.Date <= @ enddate'時會發生什麼? – 2013-02-13 10:26:10
由於丹尼爾 - 測試和語義相同,生成的查詢計劃中沒有任何更改,仍然生成約6000估計行。 – Tamar 2013-02-13 10:30:30