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

时间:2009-01-07 17:47:59

标签: sql sql-server tsql 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)

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

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

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

DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId

我得到以下跟踪统计信息

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

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

我们目前正在 SQL Server 2000

上运行

2 个答案:

答案 0 :(得分:13)

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

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?

答案 1 :(得分:-1)

是的 - 我在Oracle DB 11g上也看到了这一点 - 同样的查询在SQL提示符下的数据库服务器的2个节点上运行得很快但是从包中调用时它确实挂了!

必须清除共享池以获得相同的行为:原因是某个作业/脚本正在运行,其中旧的副本在具有较差执行计划的一个节点上的库高速缓存/内存中被锁定。