Indexing 当查询使用硬编码值时,SQL Server 2012无法使用索引

Indexing 当查询使用硬编码值时,SQL Server 2012无法使用索引,indexing,sql-server-2012,query-performance,Indexing,Sql Server 2012,Query Performance,我重构了一个SQL Server存储过程,以使用动态SQL和sp_executesql。我立刻注意到了一个巨大的性能下降——一个过去在不到一秒钟内返回的过程现在消耗了4分钟以上 经过几个小时的讨论,我终于发现,当我使用参数运行SQL语句时,它返回得很快,但使用硬编码值运行它时,它花费了很长时间。例如,此查询在不到一秒钟内返回: DECLARE @Cat VARCHAR(10); SET @Cat='Ginger'; SELECT * FROM MyTable a WHERE a.MyColum

我重构了一个SQL Server存储过程,以使用动态SQL和sp_executesql。我立刻注意到了一个巨大的性能下降——一个过去在不到一秒钟内返回的过程现在消耗了4分钟以上

经过几个小时的讨论,我终于发现,当我使用参数运行SQL语句时,它返回得很快,但使用硬编码值运行它时,它花费了很长时间。例如,此查询在不到一秒钟内返回:

DECLARE @Cat VARCHAR(10);
SET @Cat='Ginger';
SELECT * 
FROM MyTable a
WHERE a.MyColumn = @Cat 
…此查询需要4分钟:

SELECT * 
FROM MyTable a
WHERE a.MyColumn = 'Ginger' 
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N' SELECT * FROM MyTable a WHERE a.MyColumn = @Cat';
EXEC sp_executesql @SQL, N'@Cat VARCHAR(10)', @Cat
…此查询也需要4分钟:

SELECT * 
FROM MyTable a
WHERE a.MyColumn = 'Ginger' 
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N' SELECT * FROM MyTable a WHERE a.MyColumn = @Cat';
EXEC sp_executesql @SQL, N'@Cat VARCHAR(10)', @Cat
(当然,实际查询更复杂)。我假设sp_executesql在执行查询之前将@Cat参数作为硬编码值插入查询中,从而重现与非参数化查询相同的问题

查看快速查询和慢速查询在执行计划上的差异,我注意到快速查询使用了慢速查询不使用的索引。当我向慢速查询添加表提示以使用该索引时,它修复了问题,例如:

SELECT * 
FROM MyTable a WITH (INDEX(IX_MyIndex_3))
WHERE a.MyColumn = 'Ginger'
将表提示添加到动态sql查询中也解决了该问题

我的问题有两方面。第一:为什么?为什么SQL Server会将参数化查询与具有硬编码值的查询区别对待?第二,我能做些什么来避免桌子的暗示吗?出于纯粹的卫生原因,我宁愿避免在存储过程代码中添加表提示

我尝试在查询中使用的表上重建统计信息,但没有效果


这不是参数嗅探;当问题出现在存储过程中时,我可以在SSMS中手动运行查询来重现它。在任何情况下,当我在查询中使用参数时,它工作正常,当我从参数切换到硬代码值时,问题就会出现。

如果您的查询是这样构造的:

DECLARE @Cat VARCHAR(10);
SET @Cat='Ginger';
SELECT * 
FROM MyTable a
WHERE a.MyColumn = @Cat 
这是为了防止参数嗅探。在这种情况下,SQL server根据查询中列中值的分布,根据猜测的行数开发计划。这些猜测计划通常假定列值的某种均匀分布

当您这样构造查询时:

SELECT * 
FROM MyTable a WITH (INDEX(IX_MyIndex_3))
WHERE a.MyColumn = 'Ginger'

您正在向优化器提供更多信息,这可能是一件坏事。例如,如果您的列有1000行,其中有10个unqiue值,那么sql可以假设每个值出现100次,并确定哈希匹配是联接中的最佳运算符。当您将其命名为“ginger”并且统计数据显示“ginger”出现2次时,它可能会决定在没有意义的情况下使用嵌套循环运算符,从而导致性能下降。或者在未知场景中,可以使用筛选器运算符,在已知场景中,可以使用常量扫描+嵌套循环来限制行。这可能是由于在考虑“生姜”之前,计划中操作员的其他问题造成的。如上所述,提供这样的精确值通常会产生积极影响,但如果查询的其他部分没有提供足够的信息,则可能会产生消极影响。

这是在存储过程中发生的吗?。您应该在存储过程中搜索参数sniffingYes。我不认为这是参数嗅探;使用(重新编译)添加并没有修复它。这不是防止参数嗅探的唯一方法。例如,如果您的sp收到一个名为
@param
的参数,您可以使用:
DECLARE@param2 samedatatypeas原始集@param2=@param……。(此处的其余代码)
我应该注意到,在存储过程内部或在SSMS中运行语句时,也会发生这种奇怪的行为。这不是参数嗅探。优化器为带有参数的查询和带有文本值的查询生成不同的计划是正常的。通常在查询中有一个实际的文本值有助于优化器生成更好的计划。但是,在你的情况下,结果恰恰相反。显然,查询或数据分布非常复杂,即使优化器有更多的信息要处理,也会混淆优化器并生成更糟糕的计划。我看不出一个简单的解决办法。分析执行计划,注意估计行与实际行不同的区域。这往往是一个线索。