32

所以基本上我有这个相对较长的存储过程。基本的执行流程是将SELECTS INTO一些数据放入用他#签名声明的临时表中,然后在这些表中运行游标,生成一个“运行总计”到第三个临时表中,该表是使用创建的CREATE. 然后这个生成的临时表与数据库中的其他表连接起来,在一些分组等之后生成结果。问题是这个 SP 一直运行良好,直到现在在 1-2 分钟内返回结果。现在突然需要 12-15 分钟。如果我从 SP 中提取查询并通过手动设置相同的参数在管理工作室中执行它,它会在 1-2 分钟内返回结果,但 SP 需要很长时间。知道会发生什么。我尝试生成 Query 和 SP 的实际执行计划,但由于光标而无法生成它。知道为什么 SP 需要这么长时间而查询不需要吗?

4

11 回答 11

64

这是参数嗅探的足迹。有关它的另一个讨论,请参见此处;SQL差的存储过程执行计划性能——参数嗅探

有几种可能的修复方法,包括将 WITH RECOMPILE 添加到您的存储过程中,这大约有一半的时间。

大多数情况下的推荐修复方法(尽管它取决于查询和存储过程的结构)是不要直接在查询中使用参数,而是将它们存储到局部变量中,然后在查询中使用这些变量。

于 2009-08-12T13:55:51.717 回答
12

这是由于参数嗅探。首先声明临时变量并将传入的变量值设置为临时变量并在整个应用程序中使用临时变量下面是一个示例。

ALTER PROCEDURE [dbo].[Sp_GetAllCustomerRecords]
@customerId INT 
AS
declare @customerIdTemp INT
set @customerIdTemp = @customerId
BEGIN
SELECT * 
FROM   Customers e Where
CustomerId = @customerIdTemp 
End

试试这个方法

于 2014-03-06T11:02:08.677 回答
2

尝试重新编译存储过程以放弃任何存储的查询计划

exec sp_recompile 'YourSproc'

然后运行你的存储过程,注意使用合理的参数。

还要比较两种执行查询的方法之间的实际执行计划。

重新计算任何统计数据也可能是值得的。

于 2009-08-12T10:03:23.910 回答
2

我还会研究参数嗅探。可能是proc需要以不同的方式处理参数。

于 2009-08-12T13:24:43.470 回答
1

我通常通过使用“print getdate() + ' - step '”来开始解决此类问题。这有助于我缩小花费最多时间的范围。您可以从查询分析器的运行位置进行比较,并缩小问题所在。

于 2009-08-12T14:00:33.007 回答
0

我想这可能归结为缓存。如果您两次运行存储过程,第二次会更快吗?

为了进一步调查,您可以从管理工作室运行它们,存储过程和查询版本,在管理工作室中打开显示查询计划选项,然后比较存储过程中哪个区域比作为查询运行时花费的时间更长。

或者,您可以在此处发布存储过程,供人们提出优化建议。

于 2009-08-12T10:02:26.860 回答
0

首先,基于使用多个临时表(可以保存在内存中,或持久化到 tempdb ——无论 SQL Server 决定什么是最好的),SQL 无论如何都不会表现得很好。游标的使用。

我的建议是看看您是否可以将存储过程重写为基于集合的查询而不是游标方法,这将提供更好的性能并且更容易调整和优化。显然,我不确切知道您的 sproc 做了什么,以表明这对您来说有多容易/可行。

至于为什么 SP 比查询花费更长的时间 - 很难说。当您尝试每种方法时,系统上的负载是否相同?如果您在负载较轻时运行查询本身,则比在负载较重时运行 SP 时要好。

此外,为了确保查询确实比 SP 更快,您需要排除数据/执行计划缓存,这会使后续运行的查询更快。您可以使用以下命令清除缓存:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

但只能在开发/测试数据库服务器上执行此操作,而不是在生产环境中执行此操作。然后运行查询,记录统计信息(例如来自分析器)。再次清除缓存。运行 SP 并比较统计信息。

于 2009-08-12T10:05:18.007 回答
0

1) 当您第一次运行查询时,可能需要更多时间。还有一点是,如果您正在使用任何相关子查询,并且如果您对值进行硬编码,它将只执行一次。如果您没有对其进行硬编码并通过该过程运行它,并且如果您尝试从输入值中获取值,则可能需要更多时间。

2)在极少数情况下,这可能是由于网络流量造成的,而且对于相同的输入数据,我们的查询执行时间也不一致。

于 2009-08-12T10:07:40.030 回答
0

我也遇到了一个问题,我们必须创建一些临时表,然后操作它们必须根据规则计算一些值,最后将计算值插入第三个表中。这一切如果放在单个 SP 中大约需要 20-25 分钟。因此,为了进一步优化它,我们将 sp 分解为 3 个不同的 sp,现在所花费的总时间约为 6-8 分钟。只需确定整个过程中涉及的步骤以及如何将它们分解为不同的 sp。通过使用这种方法,整个过程所花费的总时间肯定会减少。

于 2010-11-17T10:01:59.290 回答
0

这是因为参数剪切。但是如何确认呢?

每当我们应该优化 SP 时,我们都会寻找执行计划。但是在您的情况下,您将看到 SSMS 的优化计划,因为仅当它通过代码调用时才需要更多时间。

对于每个 SP 和 Function,由于 ARITHABORT 选项,SQL 服务器会生成两个估计计划。一个用于 SSMS,第二个用于外部实体(ADO Net)。

ARITHABORT 在 SSMS 中默认为 OFF。因此,如果您想检查您的 SP 在从 Code 调用时使用的确切查询计划。

只需在 SSMS 中启用该选项并执行您的 SP,您将看到 SP 也需要 12-13 分钟从 SSMS。SET ARITHABORT ON EXEC YourSpName SET ARITHABORT OFF

要解决这个问题,您只需要更新估计查询计划。

有几种方法可以更新估计查询计划。1.更新表统计。2. 重新编译 SP 3. 在 SP 中设置 ARITHABORT OFF 所以它将始终使用为 SSMS 创建的查询计划(不推荐此选项)有关更多选项,请参阅这篇很棒的文章 - http://www.sommarskog.se/query-计划-mysteries.html

于 2020-01-28T15:56:22.763 回答
-1

我建议这个问题与临时表的类型(# 前缀)有关。此临时表保存该数据库会话的数据。当您通过您的应用程序运行它时,临时表将被删除并重新创建。
您可能会发现在 SSMS 中运行时,它会保留会话数据并更新表而不是创建表。希望有帮助:)

于 2009-08-12T09:57:50.707 回答