7

在工作中开发一个新查询时,我编写了它并在 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)

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

事件类持续时间 CPU 读取 写入
SQL:Stmt 已完成 75 41 7 0

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

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

我得到以下跟踪统计信息

事件类持续时间 CPU 读取 写入
SQL:Stmt 已完成 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上运行

4

2 回答 2

15

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

alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;

然后在查询中使用@p1_copy。看起来很荒谬,但它确实有效。

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

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

于 2009-01-07T17:53:09.180 回答
-1

是的——我在 Oracle DB 11g 上也看到过这一点——在 SQL 提示符下,相同的查询在 db 服务器的 2 个节点上运行得很快,但是当从包中调用它时,它真的挂了!

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

于 2015-12-31T14:05:09.283 回答