为什么参数化查询会产生比非参数化查询慢得多的查询计划

时间:2009-02-04 05:51:03

标签: sql-server

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

  

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

与以下参数化版本有着截然不同且更快的查询计划。

  

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

在我的特定情况下,具有文字的版本在亚秒级运行。参数化版本需要2-3秒。我认为它们是相同的,因为它们是相同的查询。

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

有没有办法让参数化版本具有与文字版本相同的性能?

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

4 个答案:

答案 0 :(得分:8)

查询规划器似乎已在文字查询中做出决定,该查询基于它已有的信息。它将具有可以根据特定文字中给出的数据传播有效查询的统计数据。

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

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

特别是在您的查询中,请尝试以下操作:

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))

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

答案 1 :(得分:6)

我认为你正在与“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)

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

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

答案 3 :(得分:2)

在我的情况下,DB表列类型被定义为VarChar,并且参数化查询参数类型被定义为NVarChar,这在实际执行计划中引入了CONVERT_IMPLICIT以匹配数据类型,这是母猪的罪魁祸首表现,2秒vs 11秒。只需更正参数类型,使参数化查询与非参数化版本一样快。

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