代码之家  ›  专栏  ›  技术社区  ›  Andrew Shepherd

查询运行得很快,但在存储过程中运行得很慢

  •  34
  • Andrew Shepherd  · 技术社区  · 14 年前

    我正在使用SQL 2005分析器进行一些测试。

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

    当我运行存储过程时,它需要很长的时间并执行800000次磁盘读取。

    当我对存储过程单独运行同一个查询时,它会执行14000次磁盘读取。

    由此,我假设(可能是错误的)存储过程每次都在重新编译,这就是问题的原因。

    有人能解释一下吗?

    我已经启动了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
    

    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)
    
    6 回复  |  直到 14 年前
        1
  •  79
  •   Adriaan Stander    9 年前

    好吧,我们以前也遇到过类似的问题。

    我们解决这个问题的方法是,在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
    

    这通常为我们解决了这个问题。

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

    编辑:

    Different Approaches to Correct SQL Server Parameter Sniffing 一些有见地的例子,解释和修复。

        2
  •  7
  •   ninjaPixel    11 年前
        3
  •  5
  •   Giorgi    14 年前

    我想这是由 parameter sniffing.

        4
  •  4
  •   Brian Webster Jason    12 年前

    为什么一个批处理需要永远在SQL存储过程中运行,而在SSMS中却要瞬间运行,这个问题与SQL参数嗅探有关,特别是与datetime参数有关。

    这是其中一个(我没有写,只是把它传下去)。

    http://www.sommarskog.se/query-plan-mysteries.html

        5
  •  3
  •   alaneo    8 年前

    关于我的问题我已经跑了:

    exec sp_updatestats 
    

    这使我的sp从120秒加速到3秒 https://msdn.microsoft.com/en-us/library/ms173804.aspx

        6
  •  2
  •   SumanKumar    9 年前

    我今天也有同样的问题。我已经放弃并重新创建了SP,它工作了。这与SP cache有关,当删除SP时,缓存的计划已被删除。您可以尝试相同的方法或使用“DBCC FREEPROCCACHE”删除缓存。