2013-12-13 56 views
3

我有一個包含2000萬條目的「my_table」表。該id列被編入索引。如果我做了以下查詢,它只需要少於1個SEK。SQL Server - 變量替換緩慢

SELECT * FROM my_table WHERE id='asdf' 

如果我在where子句中執行相同的查詢,但使用臨時變量。

declare @ID nvarchar(4) 
set @ID = N'asdf' 
SELECT * FROM my_table WHERE [email protected] 

它需要~14 sek。

select <- Clustered Index Scan 

執行計劃與恆:用臨時變量

執行計劃

select <- Nested Loops <- Index Seek (Non clustered) 
         <- Key Lookup (Clustered) 

第一個問題: 爲什麼第一次查詢到的是更長的時間來執行呢?
SQL Server事件探查器告訴我,第一個查詢確實〜800000次讀取(cpu 5258),而第二個查詢確實讀取了25次(cpu 0)。

第二個問題: 如果我申請OPTION(RECOMPILE)的第一個查詢,執行將運行速度快和執行計劃就像是第二,但我不明白爲什麼。
變量是否針對搜索中的每個條目進行了評估?

+1

你一直是參數嗅探的受害者。 –

+1

@ AllanS.Hansen我不認爲這是完全正確的。他是缺少參數嗅探的受害者,因爲變量的值在編譯時並不知道(除非查詢稍後會受到語句級別的重新編譯,那麼實際上會知道該值)。 –

+0

@ AllanS.Hansen - 在SQL Server中嗅探局部變量[從未發生](http://dba.stackexchange.com/a/33727/3690),沒有顯式的'RECOMPILE'提示,即使語句本身在變量被分配。 –

回答

1

當SQL Server決定使用羣集索引掃描還是使用密鑰查找的索引查找時,它會查看將返回的估計行數。

當您在查詢中將該值作爲常量提供時,SQL Server可以使用該值並查看索引的統計信息,以粗略查看該值返回的行數,在您的情況下,值asdf將是選擇性的足以使查找/查找成爲獲取數據的最快方式。

當您使用變量來保存該值時,SQL Server(在構建queryplan時)看不到變量最終會執行的值。在這種情況下,估計的行數將是基於id上索引的統計數據返回的平均行數。就你而言,SQL Server考慮查找/查詢查詢計劃時,估計的行數太高。

當您使用option (recompile)時,參數的值在創建queryplan時已知,因此SQL Server在估計行數時可以使用實際值。

如果您決定使用帶有@id的存儲過程是一個參數,那麼SQL Server能夠在編譯時使用@id的值(參數嗅探)併爲您提供該值的最佳計劃。請注意,存儲過程的查詢計劃已被緩存,因此如果稍後您使用掃描速度更快的值調用該過程,它仍將使用查找/查找來執行查詢。當然,如果第一次執行過程中的參數值將返回許多行,那麼存儲過程的計劃將執行掃描,並且只要計劃位於高速緩存中就會繼續執行掃描。