在工作中开发新查询时,我编写了它并在 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)
当我运行查询“raw”时,我得到以下跟踪统计信息
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
And when I run the query as a stored proc using the following command
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
我得到以下跟踪统计信息
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
I also get the same result if I store the query in an nvarchar and execute it using sp_executesql like this (it performs like the sproc)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
除了上面的 select 语句之外,存储过程不包含任何内容。什么会导致 sql server 仅因为该语句作为存储过程执行而选择较差的执行计划?
我们目前在 SQL Server 2000 上运行
最佳答案
这通常与参数嗅探有关。处理起来可能会非常令人沮丧。有时可以通过重新编译存储过程来解决,有时甚至可以在存储过程中使用重复变量,如下所示:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
然后在查询中使用@p1_copy。看起来很荒谬,但确实有效。
查看我最近关于同一主题的问题:
Why does the SqlServer optimizer get so confused with parameters?
关于sql - 直接执行语句和从存储过程执行语句时的执行计划不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/421275/