2009-11-03 84 views
6

從參數化查詢更改爲非參數化查詢時,我無法理解SQL Server中我的語句的估計查詢計劃的行爲。SQL Server查詢計劃差異

我有以下查詢:

DECLARE @p0 UniqueIdentifier = '1fc66e37-6eaf-4032-b374-e7b60fbd25ea' 
SELECT [t5].[value2] AS [Date], [t5].[value] AS [New] 
FROM (
    SELECT COUNT(*) AS [value], [t4].[value] AS [value2] 
    FROM (
     SELECT CONVERT(DATE, [t3].[ServerTime]) AS [value] 
     FROM (
      SELECT [t0].[CookieID] 
      FROM [dbo].[Usage] AS [t0] 
      WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0) 
      GROUP BY [t0].[CookieID] 
      ) AS [t1] 
     OUTER APPLY (
      SELECT TOP (1) [t2].[ServerTime] 
      FROM [dbo].[Usage] AS [t2] 
      WHERE ((([t1].[CookieID] IS NULL) AND ([t2].[CookieID] IS NULL)) 
      OR (([t1].[CookieID] IS NOT NULL) AND ([t2].[CookieID] IS NOT NULL) 
      AND ([t1].[CookieID] = [t2].[CookieID]))) 
      AND ([t2].[CookieID] IS NOT NULL)   
      AND ([t2].[ProductID] = @p0) 
      ORDER BY [t2].[ServerTime] 
      ) AS [t3] 
     ) AS [t4] 
    GROUP BY [t4].[value] 
    ) AS [t5] 
ORDER BY [t5].[value2] 

由LINQ2SQL表達式生成該查詢和從LINQPad萃取。這會產生一個很好的查詢計劃(據我所知),並在數據庫中執行大約10秒鐘。但是,如果我用參數替換了兩個參數,那就是用'='1fc66e37-6eaf-4032-b374-e7b60fbd25ea'替換兩個'= @ p0'部分',我得到了一個不同的估計查詢計劃,查詢現在運行得更長(超過60秒,沒有看到它通過)。

爲什麼執行看似無辜的替換會產生效率更低的查詢計劃和執行?我用'DBCC FreeProcCache'清除了程序緩存,以確保我沒有緩存壞計劃,但行爲依然存在。

我真正的問題是我可以在10秒的執行時間內生活(至少在很長的一段時間內),但我不能忍受60+秒的執行時間。我的查詢會(如上面所暗示的)通過,因此作爲

exec sp_executesql N' 
     ... 
     WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0) 
     ... 
     AND ([t2].[ProductID] = @p0) 
     ... 
     ',N'@p0 uniqueidentifier',@p0='1FC66E37-6EAF-4032-B374-E7B60FBD25EA' 

產生同樣不佳的執行時間(我認爲這是雙重奇怪,因爲這似乎是使用參數化查詢的數據庫上執行的LINQ2SQL生產。

我不是找提醒在其索引的創建或類似的,我只是想理解爲什麼查詢計劃和執行是三個看似相似的查詢,以便不同的

編輯:我已經上傳了非參數化和執行計劃用不同的GUID here

參數化查詢以及用於參數化查詢(如Heinz建議)執行計劃希望它可以幫助你幫我:)

+1

您可以發佈您收到的查詢計劃嗎?剛剛運行'SET SHOWPLAN_TEXT ON GO SELECT ...' – Quassnoi 2009-11-03 13:06:44

+0

完成...添加了執行計劃的鏈接... – 2009-11-03 13:42:03

回答

2

我不是在尋找建議創建索引或類似的東西,我只是想了解爲什麼查詢計劃和執行是如此不同的三個看似相似的查詢。

你似乎有兩個指標:

IX_NonCluster_Config (ProductID, ServerTime) 
IX_NonCluster_ProductID_CookieID_With_ServerTime (ProductID, CookieID) INCLUDE (ServerTime) 

第一指標不包括CookieID但下令ServerTime,因此對於選擇性ProductID的更有效(即那些你有很多)

第二個索引確實覆蓋了所有的列但沒有排序,因此更有效的更多選擇ProductID的(那些你很少)。

平均而言,基數使得SQL Server期望第二種方法有效,這是您使用參數化查詢或明確提供選擇性GUID時所使用的方法。

但是,你原來GUID被認爲是選擇性低,這就是爲什麼使用第一種方法。

不幸的是,第一種方法需要在CookieID額外的過濾這就是爲什麼它實際上是低效率的。

+0

啊......也許這也解釋了爲什麼如果我刪除where子句的多餘部分(檢查IS NULL的OR部分是多餘的),我會得到「快速」執行CookieID不需要額外的過濾。查詢是通過Linq2SQL生成的,我無法真正修改,但我會檢查是否可以創建Not-Null列,這似乎刪除了附加子句並生成快速查詢。 – 2009-11-03 14:37:41

1

我的猜測是,當你走非paramaterized路線,你的guid必須從一個varchar轉換爲一個UniqueIdentifier,這可能會導致一個索引不被使用,而它將被用於採用非正規路由。

我見過這種情況發生在使用查詢的where子句中使用datetime的列的smalldatetime。

3

如果您提供明確的值,SQL Server可以使用此字段的統計信息來制定「更好」的查詢計劃決策。不幸的是(正如我最近經歷的那樣),如果統計信息中包含的信息具有誤導性,有時SQL Server會做出錯誤的選擇。

如果您想深入研究此問題,我建議您檢查如果使用其他GUID會發生什麼情況:如果它針對不同的具體GUID使用不同的查詢計劃,則表示使用統計數據。在這種情況下,您可能需要查看sp_updatestats和相關命令。

編輯:看看DBCC SHOW_STATISTICS:「慢」和「快」GUID可能在直方圖中的不同桶中。我有had a similar problem,我通過在SQL中添加一個INDEX table hint來解決這個問題,它「引導」SQL Server尋找「正確的」查詢計劃。基本上,我已經查看了在「快速」查詢期間使用了哪些索引,並將這些索引硬編碼到了SQL中。這是遠遠不是一個最佳或優雅的解決方案,但我還沒有找到一個更好的...

+0

試着運行慢,非參數化的另一個GUID,它產生了一個很好的查詢計劃並按預期執行。你能否詳細說明一下我在統計方面需要尋找什麼?它是一個需要重建或類似的特定索引嗎? – 2009-11-03 13:20:36

+0

我編輯了我的帖子以添加更多詳細信息。 – Heinzi 2009-11-03 13:39:32

+0

不可否認,我第一次看到DBBC SHOW_STATISTICS,但我似乎破譯了GUID是在單獨的桶中(RANGE_ROWS等於316的「慢」和RANGE_ROWS等於0(?)的「快」)。不幸的是我使用Linq2SQL,所以我沒有設置查詢提示的真正路徑。我能否重新計算統計數據? – 2009-11-03 13:57:24

0

很難說,沒有看執行計劃,但是,如果我要猜測的原因我會說它是一個參數嗅探和糟糕的統計信息的組合 - 在將GUID硬編碼到查詢中的情況下,查詢優化器會嘗試優化對該參數值的查詢。我相信參數化/準備查詢會發生同樣的情況(這稱爲參數嗅探 - 執行計劃針對首次執行準備語句時使用的參數進行了優化),但是當您聲明時,這絕對不會發生該參數並在查詢中使用它。

就像我說的,SQL服務器試圖優化執行計劃對於那個價值,所以通常你應該看到更好的結果。在這裏看來,它基於其決策的信息是不正確的/誤導性的,並且當它優化對通用參數值的查詢時,您會更好(出於某種原因)。

這主要然而憑空猜測 - 它不可能真的說沒有執行 - 如果你可以上傳executuion計劃地方,那麼我肯定會有人能夠幫助你的真正原因。

+0

已上傳執行計劃,請參閱我編輯的文章 – 2009-11-03 14:01:17