查询运行速度很快,但在存储过程中运行缓慢

And*_*erd 36 sql-server performance stored-procedures sql-server-2005

我正在使用SQL 2005探查器进行一些测试.

我有一个存储过程,它只运行一个SQL查询.

当我运行存储过程时,它需要很长时间并执行800,000次磁盘读取.

当我对存储过程分开运行相同的查询时,它会执行14,000次磁盘读取.

我发现如果我使用OPTION(重新编译)运行相同的查询,则需要800,000次磁盘读取.

由此,我做出(可能是错误的)假设,即存储过程每次都重新编译,这就是导致问题的原因.

任何人都可以对此有所了解吗?

我已经设置了ARITHABORT.(这解决了stackoverflow上的类似问题,但没有解决我的问题)

这是整个存储过程:

CREATE PROCEDURE [dbo].[GET_IF_SETTLEMENT_ADJUSTMENT_REQUIRED]
 @Contract_ID int,
 @dt_From smalldatetime,
 @dt_To smalldatetime,
 @Last_Run_Date datetime
AS
BEGIN
 DECLARE @rv int


 SELECT @rv = (CASE WHEN EXISTS
 (
  select * from 
  view_contract_version_last_volume_update
  inner join contract_version
  on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
  where contract_version.contract_id=@Contract_ID
  and volume_date >= @dt_From
  and volume_date < @dt_To
  and last_write_date > @Last_Run_Date
 )
 THEN 1 else 0 end)

 -- Note that we are RETURNING a value rather than SELECTING it.
 -- This means we can invoke this function from other stored procedures
 return @rv
END
Run Code Online (Sandbox Code Playgroud)

这是我运行的脚本,它演示了这个问题:

DECLARE 
 @Contract_ID INT,
 @dt_From smalldatetime,
 @dt_To smalldatetime,
 @Last_Run_Date datetime,
    @rv int


SET @Contract_ID=38
SET @dt_From='2010-09-01'
SET @dt_To='2010-10-01'
SET @Last_Run_Date='2010-10-08 10:59:59:070'


-- This takes over fifteen seconds
exec GET_IF_SETTLEMENT_ADJUSTMENT_REQUIRED @Contract_ID=@Contract_ID,@dt_From=@dt_From,@dt_To=@dt_To,@Last_Run_Date=@Last_Run_Date

-- This takes less than one second!
SELECT @rv = (CASE WHEN EXISTS
(
 select * from 
 view_contract_version_last_volume_update
 inner join contract_version
 on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
 where contract_version.contract_id=@Contract_ID
 and volume_date >= @dt_From
 and volume_date < @dt_To
 and last_write_date > @Last_Run_Date
)
THEN 1 else 0 end)


-- With recompile option. Takes 15 seconds again!
SELECT @rv = (CASE WHEN EXISTS
(
 select * from 
 view_contract_version_last_volume_update
 inner join contract_version
 on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
 where contract_version.contract_id=@Contract_ID
 and volume_date >= @dt_From
 and volume_date < @dt_To
 and last_write_date > @Last_Run_Date
)
THEN 1 else 0 end) OPTION(recompile)
Run Code Online (Sandbox Code Playgroud)

Adr*_*der 77

好的,我们之前遇到过类似的问题.

我们解决这个问题的方法是在SP内部创建局部参数

DECLARE @LOCAL_Contract_ID int, 
        @LOCAL_dt_From smalldatetime, 
        @LOCAL_dt_To smalldatetime, 
        @LOCAL_Last_Run_Date datetime

SELECT  @LOCAL_Contract_ID = @Contract_ID, 
        @LOCAL_dt_From = @dt_From, 
        @LOCAL_dt_To = @dt_To, 
        @LOCAL_Last_Run_Date = @Last_Run_Date
Run Code Online (Sandbox Code Playgroud)

然后我们使用SP内部的本地参数而不是传入的参数.

这通常解决了我们的问题.

我们认为这是由于参数嗅探,但没有任何证据,对不起...... X-)

编辑:

查看一些不同的方法来纠正SQL Server参数嗅探,以获得一些有见地的示例,解释和修复.

  • 现在存储过程变得非常快,它在探查器中有0个持续时间.难以置信的.我应该担心Cargo Cult编程吗?现在我只是喜出望外了.:-) (4认同)
  • 这也解决了我的问题。从那时起,我不太确定这是怎么做的。查询运行了7秒,存储过程耗时7分钟。使用局部变量,它可以在6秒内运行。谢谢! (2认同)
  • 我花了2天的时间来提高存储过程的性能,执行过程花了1分钟和20秒,但是一旦完成更改,我只用了3秒。谢谢!:) (2认同)

nin*_*xel 6

正如其他人所提到的,这可能是一个"参数嗅探"问题.尝试包括以下内容:

OPTION (RECOMPILE)
Run Code Online (Sandbox Code Playgroud)

在SQL查询结束时.

这里有一篇文章解释了什么参数嗅探:http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx


Gio*_*rgi 5

我想这是由参数 嗅探引起的.