【问题标题】:Why does a parameterized query produces vastly slower query plan vs non-parameterized query为什么参数化查询产生比非参数化查询慢得多的查询计划
【发布时间】:2009-02-04 05:51:02
【问题描述】:

在一个 SQL Server 2005 数据库中,我正在处理这个查询:

选择 *
来自 foo
加入 bar.x = foo.x
在 baz.y 上加入 baz = foo.y
其中 foo.x = 1000

与以下参数化版本相比,查询计划大不相同且速度更快。

声明@p0 int
设置@p0 = 1000
选择 *
来自 foo
加入 bar.x = foo.x
在 baz.y 上加入 baz = foo.y
其中 foo.x = @p0

在我的特殊情况下,带有文字的版本在亚秒时间内运行。参数化版本需要 2-3 秒。我希望它们是相同的,因为它们是相同的查询。

为什么他们得到不同的查询计划?

有什么办法可以让参数化版本和字面量版本的性能一样?

这里是查询计划。我的真实查询与我上面给出的通用查询有很大不同,但是产生这些计划的两个查询之间的唯一区别是参数。为什么用参数替换文字会导致如此截然不同的计划?

【问题讨论】:

  • 您检查过实际的执行计划吗? (在 Management Studio 中使用“包含实际执行计划”)
  • 是的,它们完全不同。
  • 你会包括执行计划吗?他们可能会指出问题所在。
  • 这是另一个解决方案,适用于使用案例阻止他们实施下面列出的任何其他解决方案的任何人。我最终运行了两个查询——第一个保持参数化,但 only 返回唯一键(在我的例子中,一个名为 CTID 的列)。我的第二个查询是非参数化的,只是查询了同一个表 WHERE CTID IN(我之前提取的 CTID 列表转到此处)。根据您的情况,这可能不是解决方案,但它有效地规避了 SQL 与参数有关的问题,并将我非常大的查询从 19 秒移至 2.5 秒。

标签: sql-server


【解决方案1】:

查询规划器似乎已根据其已有的信息在文字查询中做出决定。它将具有统计信息,可以根据您的特定文字中给出的数据分布有效地查询这些统计信息。

参数化查询选择了它认为对表中所有数据最公平的查询,您会注意到这是许多嵌套循环(性能 = 差)。

也许您可以尝试在您的数据库上运行数据库优化工具,看看某些索引是否可以帮助您?

特别是在您的查询中,试试这个:

declare @p0 int
set @p0 = 1000
select *
from foo
join bar on bar.x = foo.x
join baz on baz.y = foo.y
where foo.x = @p0
OPTION ( OPTIMIZE FOR (@p0 = 1000))

但如果不确定此查询中包含的数据不会更改并且您对此计划的查询总是更有效,我会谨慎行事。

【讨论】:

  • 这似乎是正在发生的事情。 OPTIMIZE 提示使参数化查询具有与文字值相同的计划。
  • 这是不正确的。查询优化器不考虑公平性。它只是在编译时使用参数的值,但它们可能不具代表性。 OPTIMIZE FOR UNKNOWN 会有这个效果。
  • 我不同意马丁。 SQL Server 针对表中的数据(SD、spread、top 10 等)存储“统计信息”。查询计划器评估此数据以确定查询数据的最有效计划。请参阅:technet.microsoft.com/en-us/library/cc966419.aspx。这里的提示是强制计划进入“最佳”计划,因为具体的文字 1000,而不是“未知”值。
【解决方案2】:

我认为您与“parameter sniffing”有冲突。基本上,这意味着 SQL Server 尝试使用尽可能多的信息来计算查询的最佳执行计划。对于您的第一个查询,您有一个在编译时已知的常量值,因此引擎可以直接针对该值优化查询计划。

在第二个中,您在编译时使用变量掩盖了引擎中的值这一事实(您认为它应该能够弄清楚,但实际上我在一个简单的常量表达式!),导致性能不佳。

您可以尝试解决此问题的一种方法是将查询包装在一个存储过程中,该过程直接获取参数,然后将其应用于查询——如下所示:

CREATE PROCEDURE test
  @p0 int
AS
BEGIN
  select *
  from foo
  join bar on bar.x = foo.x
  join baz on baz.y = foo.y
  where foo.x = @p0
END

这应该允许优化器准确地“嗅探”您使用的参数并为您生成最佳查询计划。

【讨论】:

  • 这为我节省了 2 分钟的报告时间,而该报告需要 2.30 分钟。太棒了!
【解决方案3】:

在我的例子中,在 DB 表中,列类型被定义为 VarChar,而在参数化查询中,参数类型被定义为 NVarChar,这在实际执行计划中引入了CONVERT_IMPLICIT,以便在比较之前匹配数据类型,这是造成母猪性能的罪魁祸首, 2 秒对 11 秒。只需更正参数类型,就可以使参数化查询与非参数化版本一样快。

希望这可以帮助有类似问题的人。

【讨论】:

  • 这正是我的问题。 db 驱动程序正在将我的字符串转换为 unicode 字符串。
  • 这里也一样,我们的解决方案可以在这里找到:stackoverflow.com/a/32867579/2303366
  • 很高兴我向下滚动到这里,这正是我们的问题,导入从 1 小时缩短到 5 分钟!!
【解决方案4】:

您的起点应该是 SQL 分析器。通过探查器运行两者,并查看每种情况下的查询计划......然后更新问题以描述这两个计划。

我认为可能有问题的一件事是,如果您有一个带有一组值的参数化查询,优化器可能会查看一些统计信息/索引并选择一种方法它,然后为所有查询重用该计划 - 尽管它不是特别适合一组不同的值。同样,如果计划是在有一组数据时确定的(例如,当一个表很小时,鼓励进行表扫描)然后您添加数据桶负载,则该计划可能不合适。这些都不会影响一个查询,尽管该查询作为准备好的语句的第一个查询是糟糕的。

【讨论】:

  • 我已经包含了计划的屏幕截图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多