2014-01-28 78 views
1

我遇到了Sql Server 2008 R2的性能問題,我已經縮小到查詢優化器(我認爲!)。我正在尋找一個明確的「爲什麼發生這種情況,還是它是一個錯誤?」。查詢優化不使用索引

爲了討論的緣故,我將使用這個例子,但是在同一個場景中多個sprocs中也出現了相同的問題。 我們有一張包含付款方式的表格;關鍵字段是PaymentMethodId和UserId。 PaymentMethodId是一個int,而PK; UserId是一個具有非聚集索引的nvarchar(255)。

查詢類似於以下內容:

存儲過程PARAMS值得一提: @id INT = NULL @userId爲nvarchar(255)= NULL 有一個if語句在存儲過程的開始禁止兩個參數都是空的。

select * from PaymentMethods (nolock) pm 
where (@userId is null or @userId = pm.UserId) 
    and (@id is null or @id = pm.PaymentMethodId) 

在@userId爲空的情況下,我希望優化器檢測到第一個where子句始終爲true;如果@userId不爲空,我期望它使用UserId上的索引。 我對@id有同樣的期望。

我們看到的是,無論輸入值爲何,數據庫都會選擇執行全表掃描。雖然這涉及到其自身,但它變得更有趣。

將查詢where子句更新爲下面的等效項時,它正確使用indecies。

select * from PaymentMethods (nolock) pm 
where ((@userId is null and pm.UserId is null) OR @userId = pm.UserId) 
    and (@id is null or @id = pm.PaymentMethodId) 

這是怎麼回事?爲什麼每個記錄都會考慮「@userId爲null」,或者是真正的問題坐在他們的鍵盤前面?

+1

可能是因爲(a)你的表很小(行數很少),或者(b)因爲你使用'SELECT *',索引無法「覆蓋」該查詢,所以很多密鑰查找將是必要的,並且最終這將比僅執行全表掃描更昂貴。 –

+0

表中有1130萬條記錄,並且實際查詢通過名稱而不是*調用字段。 – Jon

+1

請發佈查詢計劃的快照。你有表格定義和樣本數據嗎?幾條記錄。 –

回答

1

可能有很多原因導致你的sp很慢。例如,存儲過程在首次運行該參數時根據參數的值創建一個計劃。這意味着即使新值可能會返回一個完全不同的結果集,也可能從另一個計劃中受益,您將得到相同的計劃。您可以嘗試使用動態SQL或使用OPTION(RECOMPILE)運行sp,以便優化程序可以創建另一個執行計劃。這是一個例子:

CREATE STORED PROCEDURE dbo.Test @userid INT, @id INT 
AS 

DECLARE @sql NVARCHAR(4000) 

SET @sql = N'SELECT * 
      FROM PaymentMethods pm 
      WHERE 1 = 1' 

SET @sql = @sql + 
      CASE 
       WHEN @userid IS NOT NULL THEN N' AND pm.UserId = @userid ' 
       ELSE N'' 
      END + 
      CASE 
       WHEN @id IS NOT NULL THEN N' AND pm.PaymentMethodId = @id ' 
       ELSE N'' 
      END 

EXEC sp_executesql @sql, N'@userid INT, @id INT', @userid, @id; 
+0

我不喜歡動態SQL作爲解決方案,但是你的關於計劃創建的意見就是我所期待的。謝謝。 我們現在決定使用if語句和單獨的查詢。 – Jon