2012-06-20 36 views
8

我有一個存儲過程,性能可怕。當我聲明一個變量時,設置它的值,然後在where子句中使用它,該語句需要花費一個小時才能運行。當我在where子句中硬編碼變量時,它在不到一秒的時間內運行。參數的性能不如硬編碼值

我開始通過執行計劃瞭解它有什麼問題。它看起來像當我嘗試傳遞一些聲明的變量時,執行計劃包含一些哈希匹配,因爲它從使用UNION和公用表表達式的視圖中選擇值。

 
/************* Begin of Stored Procedure ***************/ 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = @ColorId 
    AND 
    A.SeasonId = @SeasonId) 

END 
/************* End of Stored Procedure ***************/ 

/************* Begin of View ***************/ 
WITH Fruits (FruitId, ColorId, SeasonId) AS 
(
    -- Anchor member 
    SELECT 
     F.FruitId 
     ,F.ColorId 
     ,F.SeasonId 
    FROM 
     (( 
      SELECT DISTINCT 
       EF.FruitId 
       ,EF.ColorId 
       ,EF.SeasonId 
       ,EF.ParentFruitId 
      FROM 
       ExoticFruit EF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = EF.FruitId 
     UNION 
      SELECT DISTINCT 
       SF.FruitId 
       ,SF.ColorId 
       ,SF.SeasonId 
       ,SF.ParentFruitId    
      FROM 
       StinkyFruit SF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = SF.FruitId 
     UNION 
      SELECT DISTINCT 
       CF.FruitId 
       ,CF.ColorId 
       ,CF.SeasonId 
       ,CF.ParentFruitId 
      FROM 
       CrazyFruit CF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = CF.FruitId 

      )) f 

    UNION ALL 

    -- Recursive Parent Fruit 
    SELECT 
     FS.FruitId 
     ,FS.ColorId 
     ,FS.SeasonId 
     ,FS.ParentFruitId 
    FROM 
     Fruits FS 
     INNER JOIN MasterFruit MF 
      ON MF.[ParentFruitId] = fs.[FruitId] 
) 

SELECT DISTINCT 
    FS.FruitId 
    ,FS.ColorId 
    ,FS.SeasonId 
    FROM 
     Fruits FS 

/************* End of View ***************/ 


/* To Execute */ 
EXEC GetFruit 1,3 

如果我使用它需要一個多小時,這裏的設定值運行存儲過程的執行計劃。 With Variables

如果我運行存儲過程移除聲明和設定值,只是設置Where子句中它在不到一秒鐘的運行,這裏下面的語句是執行計劃:

WHERE(A.ColorId = 1 AND A.SeasonId = 3) 

hard coded where clause

請注意硬編碼變量在第一次使用散列集時如何使用索引。這是爲什麼?爲什麼where子句中的硬編碼值與聲明的變量不同?

-------這就是最後用@ user1166147 ------的幫助

我改變了存儲過程使用sp_executesql的執行。

 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

DECLARE @SelectString nvarchar(max) 

SET @SelectString = N'SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = ' + CONVERT(NVARCHAR(MAX), @ColorId) + ' 
    AND 
    A.SeasonId = ' + CONVERT(NVARCHAR(MAX), @SeasonId) + ')' 

EXEC sp_executesql @SelectString 

END 
+0

你檢查,以確保您的參數的數據類型匹配列的數據類型? – HackedByChinese

+2

這些變量不是參數。 SQL Server不會執行變量嗅探,因此選擇性估計將會是猜測。如果添加「OPTION(RECOMPILE)」? –

+1

使用選項重新編譯。有一種叫做參數嗅探的方法,其中sql server根據輸入值生成不同的查詢計劃 – praveen

回答

2

編輯總結每一個請求從Damien_The_Unbeliever

的目標是獲取有關變量的值最好/最信息SQL之前創建的計劃,一般參數嗅探做到這一點。在這種情況下,可能有一個原因是參數嗅探是「禁用的」。在沒有看到實際代碼的更好表示的情況下,我們無法確切地說出解決方案或存在問題的原因。嘗試下面的內容來強制受影響的地區使用實際值生成計劃。

* 更多的細節*

長版這是您的實際存儲過程?你有參數的默認值嗎?如果是這樣,他們是什麼?

參數嗅探可以幫助 - 但它必須具有典型​​的參數值才能很好地創建計劃,如果沒有,將不會真的幫助或根據非典型參數值創建一個錯誤計劃。因此,如果一個變量的默認值爲null,或者第一次運行並編譯計劃時的值不是典型值,則會創建一個錯誤的計劃。

如果有人寫了這個sproc - 他們可能故意'禁用'參數嗅探與局部變量的原因。業務規則可能需要這些變量結構。

目標是在創建計劃之前獲取有關變量值的最佳/大多數信息,並且通常參數嗅探會執行此操作。但有些事情可能會使其對性能產生負面影響,這可能是它「被禁用」的原因。它仍然看起來像正在創建的計劃與參數非典型值或沒有足夠的信息仍然 - 使用參數嗅探或不。

嘗試在sproc中調用查詢使用sp_executesql執行受影響的查詢,迫使它爲實際變量生成該區域的計劃,並查看它是否更好。如果必須具有這種不規則的參數值,那麼這可能是您的解決方案 - 創建存儲過程並運行受影響的部件,並在變量接收到典型值後在存儲過程中稍後調用它們。

沒有看到實際代碼的更好表示,很難看出問題是什麼。希望這個信息會有所幫助 -

+0

雖然你在編輯心情 - 目前,這只是一個文本牆。也許嘗試分解成段落? –

+0

這不是實際的存儲過程。它的準確率達到了95%,它只是改變了列名,使得表名和列保持私有,因爲它是一個財務系統。 *我知道這很糟糕。我很抱歉我必須這樣做,但盡我所能儘可能使其儘可能地接近最小變化 – Mark

+0

通過使用「sp_executesql」,我能夠在不到一秒的時間內完成它。我會爲未來的用戶提出解決方案。 – Mark

0

你可以強制它基於你可能更清楚的典型值來優化你的查詢。 要將原始查詢添加以下內容:

OPTION (OPTIMIZE FOR(@ColorId = 1, @SeasionId = 3)) 

它必須不帶動態SQL類似的效果。 如果你不知道的典型值,可以使優化嗅探他們:

OPTION (OPTIMIZE FOR UNKNOWN) 

同樣,沒有動態SQL