【问题标题】:SQL Server stored procedure a lot slower than straight querySQL Server 存储过程比直接查询慢很多
【发布时间】:2012-10-22 04:47:23
【问题描述】:

我有一张表,里面有超过 100MM 的记录。该表有一个聚集索引和一个非聚集索引。

我可以使用 T-SQL 在表上运行基本计数,运行时间为 1 秒。当我将相同的精确计数查询放入存储过程中时,它需要 12 秒才能运行。

我查看了标准查询和存储过程的执行计划,它们都使用了非聚集索引。

我不确定为什么存储过程与标准查询相比如此缓慢。

我已经阅读了一些关于在这种情况下重新索引的内容,但我不确定我为什么需要这样做。此外,重新索引需要几个小时,所以我想确保它能够正常工作。

在这方面的任何帮助都会很棒。

谢谢

更新

这是存储过程:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE quickCount 

@sYID INT,
@eYID INT

AS
BEGIN

SET NOCOUNT ON;


    SELECT COUNT(leadID)
    FROM dbo.leads
    WHERE yearID >= @sYID
    AND yearID <= @eYID

END
GO

这是标准查询:

SELECT COUNT(leadID)
FROM leads
WHERE yearID >= 0
AND yearID <= 99

我确实尝试在没有参数的情况下运行它,并且 SP 运行得更快(1 秒)。所以我假设它与参数有关。

【问题讨论】:

  • 能否给我们看一下SP,有没有传入任何参数?
  • 我认为您需要显示您的查询和 SP。
  • 您可能会尝试使用常量进行查询,而您的过程使用参数而不是此常量。我建议您创建 SQLFiddle 示例或发布有关架构和查询/sp 的一些详细信息
  • 您是否针对“冷”页面缓冲区运行两者?或者你先跑这两个中的哪一个?在运行这两者中的任何一个之前,我会尝试执行 DBCC DROPCLEANBUFFERSDBCC FREEPROCCACHE - 执行时间差异是否仍然存在?还是现在两者速度一样?
  • Afaik 没关系,但您可以尝试使用 SET ANSI_NULLS ON 进行简单查询吗?

标签: sql-server performance stored-procedures


【解决方案1】:

尝试将您的 SP 更改为使用传入变量的本地副本。

类似

ALTER PROCEDURE quickCount  

@sYID INT, 
@eYID INT 

AS 
BEGIN 

SET NOCOUNT ON; 
    DECLARE @Local_sYID INT, 
            @Local_eYID INT 
    SELECT  @Local_sYID = @sYID INT, 
            @Local_eYID = @eYID INT

    SELECT COUNT(leadID) 
    FROM dbo.leads 
    WHERE yearID >= @Local_sYID 
    AND yearID <= @Local_eYID 

END 

我之前发现由于参数嗅探,SP 运行速度可能会慢很多,但是一旦使用变量的副本,性能就会恢复。

What is Parameter Sniffing ?

SQL Server : Parameter Sniffing

【讨论】:

  • 感谢您的帮助,但我已经尝试过了。它并没有让它变得更快。
  • 哇!感谢那!在我找到这篇文章之前,我无法在我的 SP 中找到问题!非常感谢!
【解决方案2】:

如前所述,这可能是一个参数嗅探问题。尝试包括以下行:

OPTION (RECOMPILE)

在您的 SQL 查询结束时。

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

【讨论】:

    【解决方案3】:

    您总是可以尝试将其作为动态 sql 执行:

    ALTER PROCEDURE quickCount  
    
    @sYID INT, 
    @eYID INT 
    
    AS 
    BEGIN 
        SET NOCOUNT ON;
    
        DECLARE @SQL VARCHAR(max)
    
        SELECT @SQL = '
        SELECT COUNT(leadID) 
        FROM dbo.leads 
        WHERE yearID >= '+CONVERT(VARCHAR(20),@sYID)+'
        AND yearID <=   '+CONVERT(VARCHAR(20),@eYID)
    
        EXEC (@SQL)
    END 
    

    【讨论】:

      【解决方案4】:

      第一次运行存储过程时,SQL Server 必须编译存储过程,这可能需要一些时间。 @Astander 提到了参数嗅探——这是一个有效的观点,并且可能会扭曲您的结果。

      需要考虑的其他一些因素是(虽然它们不应该真正解释您的症状):

      • 您可以强制锁定级别,例如WITH (NOLOCK) 在表名之后,这可以解决问题(但请注意,这样做可能会得到不准确的结果)。
      • 您可能需要更新表的统计信息或对索引进行碎片整理

      【讨论】:

        【解决方案5】:
        EXEC sp_recompile N'dbo.MyStoredProc';
        

        为我做了诀窍。存储过程需要 13 秒才能运行,查询需要 0.00 秒。运行上述命令后,它们都需要 0.00 秒 :)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-09-01
          • 1970-01-01
          • 2012-06-20
          • 1970-01-01
          • 1970-01-01
          • 2015-02-23
          • 1970-01-01
          相关资源
          最近更新 更多