将常量移动到 where 子句中的变量会显着改变执行计划吗?

Move the constant to variable in the where clause dramatically change the execution plan?

我为复杂视图创建了索引。 运行 Sql Server management studio 中的以下查询需要 0 到几秒。并且查询计划显示 99% 的成本是在我为主要 big table 创建的索引上的 Index Seek 上。 (总子树成本为 7.5)

select * from ComplexView where id = 10000 and theDate = '1/1/2018'

但是,下面的查询

declare @id int = 10000, @theDate datetime = '1/1/2018'
select * from ComplexView where id = @id and theDate = @theDate

需要很长时间(3 到 10 分钟),查询计划捕获显示 57% 的成本在 Table 主要大 table 上扫描(加上 10% 过滤器它和 14% 的哈希匹配和 17% 的排序,总子树成本为 260)。

第一个查询将根据您的文字 10000'1/1/2018' 的值以及所涉及表的直方图统计信息编译查询计划。

第二个本质上是 "optimise for unknown",因为参数值在编译时被认为是未知的。当您使用局部变量 SQL 服务器无法再使用直方图。相反,它使用有关统计对象的密度向量的信息。

这是已知和预期的行为(尽管如果您不知道它可能会令人惊讶)。

您为两个查询打开了显示实际执行计划,发现它们有不同的计划。

如果您碰巧首先 运行 一个具有 'typical' 值的查询(基于所涉及的列的分布),您将获得适用于过滤器的大多数值的缓存计划。

假设您的统计数据是最新的,试试这个:

declare @id int = 10000, @theDate datetime = '1/1/2018'
select * from ComplexView 
where id = @id and theDate = @theDate
option(recompile)

有没有更合理的方案?

使用 option(recompile) 是 'it depends' 答案之一!如果查询是一个报告查询(我的意思是它相对较长 运行ning 而不是 运行 频繁)然后考虑添加它。它告诉 SQL 服务器不要缓存计划并在每次执行时重新编译新计划。它的缺点是您不会在缓存中看到计划,并且您在每次执行时支付(与长 运行ning 查询相比相对较小)计划编译成本。

如果它是一个关键的报告查询,那么我会添加 option(recompile) 以确保您永远不会得到不合适的查询计划。