2015-07-10 57 views
2

我在我的Microsoft SQL Server(2012年快遞)數據庫運行下面的SQL查詢,並能正常工作,在不到一秒鐘的執行:簡單的變化會導致SQL查詢執行時間大幅增加

SELECT 
    StringValue, COUNT(StringValue) 
FROM Attributes 
WHERE 
    Name = 'Windows OS Version' 
    AND StringValue IS NOT NULL 
    AND ProductAssociation IN (
    SELECT ID 
    FROM ProductAssociations 
    WHERE ProductCode = 'MyProductCode' 
) 
GROUP BY StringValue 

我在內部查詢中添加一個過濾器,它會繼續正常工作,返回的結果稍微少一些(如預期的那樣),並且在不到一秒的時間內執行。

SELECT 
    StringValue, COUNT(StringValue) 
FROM Attributes 
WHERE 
    Name = 'Windows OS Version' 
    AND StringValue IS NOT NULL 
    AND ProductAssociation IN (
    SELECT ID 
    FROM ProductAssociations 
    WHERE ProductCode = 'MyProductCode' 
    AND ID IN (
     SELECT A2.ProductAssociation 
     FROM Attributes A2 
     WHERE A2.Name = 'Is test' AND A2.BooleanValue = 0 
    ) 
) 
GROUP BY StringValue 

但是當我添加一個標誌變量,使我在內部查詢到「開啓/關閉」過濾器,標誌設置爲零,查詢似乎會無限期地執行(我離開運行約5分鐘後強制取消):

DECLARE @IsTestsIncluded bit 
SET @IsTestsIncluded = 0 

SELECT 
    StringValue, COUNT(StringValue) 
FROM Attributes 
WHERE 
    Name = 'Windows OS Version' 
    AND StringValue IS NOT NULL 
    AND ProductAssociation IN (
    SELECT ID 
    FROM ProductAssociations 
    WHERE ProductCode = 'MyProductCode' 
    AND (
     @IsTestsIncluded = 1 
     OR 
     ID IN (
     SELECT A2.ProductAssociation 
     FROM Attributes A2 
     WHERE A2.Name = 'Is test' AND A2.BooleanValue = 0 
    ) 
    ) 
) 
GROUP BY StringValue 

爲什麼?我究竟做錯了什麼?我發誓我過去沒有使用過這種模式。

(當我設置@IsTestsIncluded = 1在最終查詢的上方,過濾器被跳過,並且執行時間是正常的 - 延遲只發生時@IsTestsIncluded = 0


EDIT

作爲每Joel的請求在的意見,這裏是第一個查詢的執行計劃:

Execution plan for first query

這裏是第二查詢執行計劃:

enter image description here

(我不能發佈一個執行計劃,因爲它永遠不會完成第三查詢 - 除非有另一種方式得到它在SSMS? )

+2

這是無法回答的,直到你還發布執行計劃(有投票關閉,抱歉) ,但是**猜測**(因此評論而不是答案)是'OR'條件的加入破壞了服務器可靠地使用過濾器索引的能力。 –

+1

此外,不是在'IN'子句中嵌套'SELECT',而是通過在子查詢上將它重寫爲'JOIN',幾乎可以做得更好。 –

+0

我按照你的建議(在第一個子查詢上)使用JOIN,但結果仍然相同。將發佈執行計劃。 – Ross

回答

3

試試這個:

SELECT 
    a.StringValue, COUNT(a.StringValue) 
FROM Attributes a 
INNER JOIN ProductAssociations p ON a.ProductAssociation = p.ID 
    AND p.ProductCode = 'MyProductCode' 
LEFT JOIN Attributes a2 ON a2.ProductAssociation = p.ID 
    AND a2.Name = 'Is Test' AND a2.BooleanValue = 0  
WHERE 
    Name = 'Windows OS Version' 
    AND StringValue IS NOT NULL 
    AND COALESCE(a2.ProductAssociation, NULLIF(@IsTestsIncluded, 1)) IS NOT NULL 
GROUP BY a.StringValue 

coalesce/nullif組合是不是最容易遵循的事情我曾經寫過,但它應該是功能上等同於你有什麼,只要連接條件匹配0或1個記錄rd在連接的桌子上。

+0

嗨喬爾 - 這似乎工作 - 非常感謝!你的代碼唯一的問題是:(1)該標誌是相反的,所以我將你的NULLIF改爲NULLIF(@IsTestsIncluded,0),(2)我必須在你的WHERE子句中用「a」前綴Name和StringValue。 - 當你不是執行的時候容易錯過! – Ross

5

爲什麼?我究竟做錯了什麼?

您正在嘗試編譯需要根據變量滿足多個不同條件的查詢。優化者必須拿出一個計劃在工作都案件。

儘量避免像瘟疫一樣。只需發出兩個查詢,一個條件爲另一個條件,以便優化程序可以分別優化每個查詢,並且編譯執行計劃對於每種情況都是最優的。

話題的討論lenghty,替代品和優缺點:喬爾+1

Dynamic Search Conditions in T‑SQL

+0

感謝Remus--這顯然是我需要了解更多信息。將該鏈接添加到我的週末閱讀列表中。 – Ross

-1

好的答案,或者是難以優化

再回到第二
在哪裏在優化器難以優化
考慮加入所有那些在
這仍然有一個OR可能會導致錯誤的查詢計劃,但它會給予優化在最小化或

SELECT A1.StringValue, COUNT(A1.StringValue) 
FROM Attributes A1 
JOIN ProductAssociations PA 
    ON PA.ID = A1.ProductAssociation 
    AND A1.Name = 'Windows OS Version' 
    AND A1.StringValue IS NOT NULL 
    AND PA.ProductCode = 'MyProductCode' 
JOIN Attributes A2 
    ON A2.ProductAssociation = A1.ProductAssociation 
    AND ( @IsTestsIncluded = 1 
     OR (A2.Name = 'Is test' AND A2.BooleanValue = 0) 
    ) 
GROUP BY A1.StringValue 

IZER一個更好的機會,如果你重構@IsTestsIncluded你也許可以做到這一點

SELECT A1.StringValue, COUNT(A1.StringValue) 
FROM Attributes A1 
JOIN ProductAssociations PA 
    ON PA.ID = A1.ProductAssociation 
    AND A1.Name = 'Windows OS Version' 
    AND A1.StringValue IS NOT NULL 
    AND PA.ProductCode = 'MyProductCode' 
LEFT JOIN Attributes A2 
    ON A2.ProductAssociation = A1.ProductAssociation 
    AND A2.Name = 'Is test' 
    AND A2.BooleanValue = 0 
WHERE ISNULL(@IsTestsIncluded, A2.ProductAssociation) is NOT NULL 
GROUP BY A1.StringValue 
+1

來吧投票是什麼問題? – Paparazzi