【问题标题】:Parameter doesn't perform as well as hard coding the value参数的性能不如硬编码值
【发布时间】:2012-06-22 13:58:31
【问题描述】:

我有一个性能非常糟糕的存储过程。当我声明一个变量时,设置它的值,然后在 where 子句中使用它,该语句需要一个多小时才能运行。当我在 where 子句中对变量进行硬编码时,它会在不到一秒的时间内运行。

我开始通过执行计划来研究它的问题所在。看起来当我尝试将一些声明的变量传递给它时,执行计划会创建一些哈希匹配,因为它从使用 UNION 和公共表表达式的视图中选择值。

/************* 存储过程的开始 ******************/ 创建程序 GetFruit @ColorId 大整数, @SeasionId 大整数 重新编译 作为 开始 选择 一个名字 从 [Apple_View] A /* 这是下面的视图 */ INNER JOIN [水果] F 开(F.ColorId = @ColorId AND A.FruitId = F.FruitId) 在哪里 (A.ColorId = @ColorId 和 A.SeasonId = @SeasonId) 结尾 /************* 存储过程结束 **************/ /************* 视图开始 ***************/ WITH Fruits (FruitId, ColorId, SeasonId) AS ( -- 主播成员 选择 F.FruitId ,F.ColorId ,F.SeasonId 从 (( 选择不同的 EF.FruitId ,EF.ColorId ,EF.SeasonId ,EF.ParentFruitId 从 异国水果英孚 INNER JOIN水果FR ON FR.FruitId = EF.FruitId 联盟 选择不同的 SF.FruitId ,SF.ColorId ,SF.SeasonId ,SF.ParentFruitId 从 臭果SF INNER JOIN水果FR ON FR.FruitId = SF.FruitId 联盟 选择不同的 CF.FruitId ,CF.ColorId ,CF.SeasonId ,CF.ParentFruitId 从 疯狂水果CF INNER JOIN水果FR ON FR.FruitId = CF.FruitId )) F 联合所有 -- 递归父果 选择 FS.FruitId ,FS.ColorId ,FS.SeasonId ,FS.ParentFruitId 从 水果FS INNER JOIN MasterFruit MF ON MF.[ParentFruitId] = fs.[FruitId] ) 选择不同的 FS.FruitId ,FS.ColorId ,FS.SeasonId 从 水果FS /************* 视图结束 ***************/ /* 执行 */ 执行 GetFruit 1,3

如果我使用设置的值运行存储过程需要一个多小时,这里是执行计划。

如果我运行存储过程删除 DECLARE 和 SET 值并将 Where 子句设置为以下语句,它将在不到一秒的时间内运行,这是执行计划:

WHERE(A.ColorId = 1 AND  A.SeasonId = 3)

注意硬编码变量如何使用索引,而第一个变量使用哈希集。这是为什么?为什么 where 子句中的硬编码值与声明的变量不同?

--------这是在@user1166147的帮助下最终完成的------

我将存储过程更改为使用 sp_executesql。

创建程序 GetFruit @ColorId 大整数, @SeasionId 大整数 重新编译 作为 开始 声明 @SelectString nvarchar(max) SET @SelectString = N'SELECT 一个名字 从 [Apple_View] A /* 这是下面的视图 */ INNER JOIN [水果] F 开(F.ColorId = @ColorId AND A.FruitId = F.FruitId) 在哪里 (A.ColorId = ' + CONVERT(NVARCHAR(MAX), @ColorId) + ' 和 A.SeasonId = ' + CONVERT(NVARCHAR(MAX), @SeasonId) + ')' 执行 sp_executesql @SelectString 结尾

【问题讨论】:

  • 这些是变量而不是参数。 SQL Server 不进行变量嗅探,因此选择性估计将是猜测。如果添加OPTION (RECOMPILE) 会怎样?
  • 使用选项重新编译。有一种叫做参数嗅探的东西,sql server 根据输入值生成不同的查询计划
  • 有趣的是,我没有注意到它们是局部变量。除了使用OPTION RECOMPILEoptimize for ad hoc workloads 设置之外,我过去还通过将局部变量声明移动到参数列表以及默认值来克服这个问题,从而使优化器更具可见性。
  • 您可能会在dba.stackexchange.com上获得更多里程
  • 很确定这只是因为疯狂的 cmets 而不是问题的质量。提供的信息很少,实际有助于解决问题,即数据的分布和真正的 SQL。恕我直言,这在当前的问题形式中无法解决,抱歉。

标签: sql sql-server tsql sql-server-2005 stored-procedures


【解决方案1】:

编辑摘要根据 Damien_The_Unbeliever 的要求

目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探会执行此操作。在这种情况下,参数嗅探被“禁用”可能是有原因的。在没有看到实际代码的更好表示的情况下,我们无法真正说出解决方案是什么或问题存在的原因。尝试以下方法,强制受影响区域使用实际值生成计划。

*更多细节的长版*

这是您实际的存储过程吗?你的参数有默认值吗?如果是这样,它们是什么?

参数嗅探可以提供帮助 - 但它必须具有典型​​的参数值才能很好地创建计划,如果没有,将不会真正有帮助,或者会根据非典型参数值创建一个糟糕的计划。因此,如果一个变量在第一次运行和编译计划时具有默认值 null 或不是典型值 - 它会创建一个错误的计划。

如果其他人编写了这个存储过程 - 他们可能出于某种原因故意“禁用”了对局部变量的参数嗅探。业务规则可能需要这些可变结构。

目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探会执行此操作。但是有些事情可能会使它对性能产生负面影响,这可能就是它被“禁用”的原因。似乎仍在使用参数的非典型值或仍然没有足够的信息来创建计划 - 是否使用参数嗅探。

尝试使用使用 sp_executesql 调用存储过程中的查询来执行受影响的查询,强制它使用实际变量为该区域生成一个计划,看看它是否更好。如果您必须拥有这种不规则的参数值,这可能是您的解决方案 - 创建运行受影响部分的存储过程并稍后从存储过程中调用它们 - 在变量接收到典型值之后。

如果没有更好地表示实际代码,就很难看出问题所在。希望这些信息会有所帮助 -

【讨论】:

  • 当您正在编辑时 - 目前,这只是一堵文字墙。也许尝试把它分成几段?
  • 这不是实际的存储过程。它是 95% 的准确率,它只是更改了列名以保持表名和列的私密性,因为它是一个金融系统。 *我知道这很糟糕,而且更难。很抱歉我不得不这样做,但我已尽我所能通过最小的更改使其尽可能接近
  • 通过使用“sp_executesql”,我能够在不到一秒的时间内完成它。我会在我的问题中为未来的用户提出解决方案。
  • 我明白了。显然,您不能发布您的公司详细信息。我尽我所能猜测,我很高兴它更好。
【解决方案2】:

您可以强制它根据您可能更了解的典型值来优化您的查询。 在您的原始查询中添加以下内容:

OPTION (OPTIMIZE FOR(@ColorId = 1, @SeasionId = 3))

如果没有动态 SQL 也会有类似的效果。 如果你不知道典型值,你可以让优化器嗅探它们:

OPTION (OPTIMIZE FOR UNKNOWN)

同样,没有动态 SQL

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多