当查询使用硬编码值

时间:2016-11-02 19:42:57

标签: 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.MyColumn = @Cat 

...虽然此查询需要4分钟:

SELECT * 
FROM MyTable a
WHERE a.MyColumn = 'Ginger' 

...此查询也需要4分钟:

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中手动重现它运行查询。在任何情况下,当我在查询中使用参数时,它工作正常,当我从参数切换到硬编码值时,问题就出现了。

1 个答案:

答案 0 :(得分:0)

如果您的查询结构如下:

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次,并确定哈希匹配是连接中的最佳运算符。当你给它'生姜'并且统计数据显示'生姜'出现2次时,它可能决定在没有意义并导致性能下降的情况下使用嵌套循环操作符。或者在未知场景中可以使用过滤器运算符,并且在已知场景中,可以使用恒定扫描+嵌套循环来限制行。这可能是由于在考虑“生姜”的部分之前计划中的操作员的其他问题引起的。如上面的评论中所述,提供这样的精确值通常会产生积极影响,但如果查询的其他部分未提供足够的信息,则会产生负面影响。