参数的执行速度不如硬编码值

Mar*_*ark 7 sql t-sql sql-server stored-procedures sql-server-2005

我有一个执行非常糟糕的存储过程.当我声明一个变量时,设置它的值,然后在where子句中使用它,该语句需要一个多小时才能运行.当我在where子句中对变量进行硬编码时,它会在不到一秒的时间内运行.

我开始通过执行计划调查它出了什么问题.看起来当我尝试传递一些声明的变量时,执行计划会创建一些Hash Match,因为它从使用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

如果我使用设置值运行存储过程,则需要一个多小时,这是执行计划. 随着变量

如果我运行存储过程删除DECLARE和SET值并将Where子句设置为以下语句,它将在不到一秒的时间内运行,这是执行计划:

WHERE(A.ColorId = 1 AND  A.SeasonId = 3)
Run Code Online (Sandbox Code Playgroud)

硬编码的where子句

注意硬编码变量如何使用索引,而第一个使用哈希集.这是为什么?为什么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

use*_*147 2

编辑摘要 根据 Damien_The_Un believer 的要求

目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探就是这样做的。在这种情况下,参数嗅探被“禁用”可能是有原因的。如果没有看到实际代码的更好表示,我们就无法真正说出解决方案是什么或问题存在的原因。请尝试以下操作,强制受影响的区域使用实际值生成计划。

*包含更多细节的长版 *

这是您实际的存储过程吗?你的参数有默认值吗?如果有,它们是什么?

参数嗅探可以提供帮助 - 但它必须具有典型​​的参数值才能很好地创建计划,如果没有,则不会真正有帮助,或者会根据非典型参数值创建一个糟糕的计划。因此,如果变量的默认值为 null 或在第一次运行和编译计划时不是典型值,则会创建一个错误的计划。

如果其他人编写了这个存储过程 - 他们可能出于某种原因故意“禁用”局部变量的参数嗅探。业务规则可能需要这些可变结构。

目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探会执行此操作。但有些事情可能会使其对性能产生负面影响,这可能就是它被“禁用”的原因。看起来该计划仍然是使用非典型参数值创建的,或者仍然没有足够的信息 - 使用或不使用参数嗅探。

尝试使用 sp_executesql 调用存储过程内的查询来执行受影响的查询,强制它使用实际变量为该区域生成计划,并查看是否更好。如果您必须拥有这种不规则的参数值,这可能是您的解决方案 - 创建运行受影响部分的存储过程,并稍后在变量收到典型值后从存储过程中调用它们。

如果没有看到实际代码的更好表示,就很难看出问题所在。希望这些信息会有所帮助 -