直接执行语句和从存储过程执行时的不同执行计划

Mar*_*son 6 sql t-sql sql-server stored-procedures sql-execution-plan

在工作中开发新查询时,我编写了它并在SQL查询分析器中对其进行了分析.查询在没有任何表扫描的情况下表现非常好但是当我将其封装在存储过程中时,性能非常糟糕.当我查看执行计划时,我可以看到SQL Server选择了一个不同的计划,使用表扫描而不是TableB上的索引查找(我被迫混淆了表和列名,但没有查询逻辑已经改变).

这是查询

SELECT     
    DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day, 
    DATEPART(hh, TableA.Created) AS [Hour], 
    SUM(TableB.Quantity) AS Quantity, 
    SUM(TableB.Amount) AS Amount
FROM
    TableA
    INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE     
    (TableA.ShopId = @ShopId)
GROUP BY 
    DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)), 
    DATEPART(hh, TableA.Created)
ORDER BY 
    DATEPART(hh, TableA.Created)
Run Code Online (Sandbox Code Playgroud)

当我运行查询"raw"时,我得到以下跟踪统计信息

Event Class         Duration  CPU  Reads Writes
SQL:StmtCompleted   75        41      7      0

当我使用以下命令将查询作为存储过程运行时

DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
Run Code Online (Sandbox Code Playgroud)

我得到以下跟踪统计数据

Event Class         Duration  CPU  Reads Writes
SQL:StmtCompleted   222       10     48      0

如果我将查询存储在nvarchar中并使用像这样的sp_executesql执行它(它像sproc一样执行),我也得到相同的结果

DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
Run Code Online (Sandbox Code Playgroud)

除上面的select语句外,存储过程不包含任何内容.什么会导致SQL Server选择劣质执行计划只是因为该语句作为存储过程执行?

我们目前正在运行SQL Server 2000

Eri*_*ard 13

这通常与参数嗅探有关.处理可能非常令人沮丧.有时它可以通过重新编译存储过程来解决,有时您甚至可以在存储过程中使用重复变量,如下所示:

alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
Run Code Online (Sandbox Code Playgroud)

然后在查询中使用@ p1_copy.似乎很荒谬,但它的确有效.

查看我最近关于同一主题的问题:

为什么SqlServer优化器会与参数混淆?