我正在使用SQL 2005探查器进行一些测试。
我有一个只运行一个SQL查询的存储过程。
当我运行存储过程时,需要很长时间并执行800,000次磁盘读取。
当我对存储过程分开运行相同的查询时,它会执行14,000次磁盘读取。
我发现如果我使用OPTION(重新编译)运行相同的查询,则需要800,000次磁盘读取。
由此,我做出(可能是错误的)假设,即存储过程每次都重新编译,这导致了问题。
任何人都可以对此有所了解吗?
我已设置ARITHABORT ON。 (这解决了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
这是我运行的一个脚本,用于演示问题:
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)
答案 0 :(得分:71)
好的,我们之前遇到过类似的问题。
我们修复此问题的方法是在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
然后我们使用SP内部的本地参数而不是传入的参数。
这通常解决了我们的问题。
我们认为这是由于参数嗅探,但没有任何证据,对不起...... X - )
修改强>
请查看Different Approaches to Correct SQL Server Parameter Sniffing,了解一些有见地的示例,解释和修复。
答案 1 :(得分:6)
正如其他人所提到的,这可能是一个“参数嗅探”问题。尝试包括以下行:
OPTION (RECOMPILE)
在SQL查询结束时。
这里有一篇文章解释了什么参数嗅探:http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx
答案 2 :(得分:5)
答案 3 :(得分:4)
为什么批处理永远在SQL存储过程中运行但在SSMS中即时运行的问题与SQL参数嗅探有关,尤其是对于datetime参数。
有几篇关于参数嗅探的优秀文章。
这是其中之一(我没有写它,只是传递它)。
答案 4 :(得分:3)
关于我的问题,我跑了:
OldClass
这使我的速度从120秒加速到3秒。有关更新统计信息的更多信息,请访问https://msdn.microsoft.com/en-us/library/ms173804.aspx
答案 5 :(得分:2)
我今天也遇到了同样的问题。我已经删除并重新创建了SP并且它有效。这是SP缓存,当删除SP时,缓存的计划已被删除。您可以尝试相同或使用“DBCC FREEPROCCACHE”删除缓存。