我有一张桌子,我们称之为History
。主键(也称为聚簇索引)称为HIST_ID
。该表在开发DB中有大约2300行。现在考虑以下两个查询:
查询1:
declare @x int
set @x = 14289
select * from History where hist_id=@x
查询2:
declare @x int
set @x = 14289
select * from History where hist_id=@x or @x is null
唯一的区别是最后的or @x is null
。然而,第一个查询执行索引搜索,第二个 - 索引扫描。是什么给了什么?
先发制人的回复 - 不,选项(重新编译)没有帮助。
补充:我想要一些可靠的论证事实,而不是猜测。我自己可以猜出十几种可能的原因。但这里的真正的问题是什么?
答案 0 :(得分:1)
我建议将计划与传入/使用的参数分开生成,因此实质上有一个要求(取决于@x的值)返回每一行。因此,查询计划正在处理它可以接收的参数的最坏情况。
e.g。如果@x的输入为null,则查询将被强制返回每一行,因为每一行都将满足始终返回true的文字方程/谓词。要使查询计划涵盖@x的每个值,它必须生成执行扫描的计划。
答案 1 :(得分:0)
我想优化器确定它是有益的。另一种方法是使用与编写
相同的计划select * from History where hist_id=@x
union all
select * from History where @x is null
您可以用这种方式重写查询,但我很确定优化器能够自行完成此操作。你有多少个空值?
编辑:结果我误解了这个问题,并认为你想要WHERE(@x = hist_id或hist_id为null)。实际上你想要一个动态标准。查看this article。请注意,如果您指定WITH(RECOMPILE),那么您的查询应该在SQL2k8中工作,但由于一个讨厌的错误,此支持已被删除。
答案 2 :(得分:0)
当然是索引扫描。
聚簇索引扫描=表扫描,因为“@x IS NULL”没有合理的谓词。
参数化的缓存计划是通用的,适用于@x = NULL或@x = value。 如果你没有定义@x,你应该得到相同的计划。
如果编码“12345 IS NULL”,则会检测到并忽略它。
我找不到关于如何在查询计划中处理常量的博客文章。要点是它们是一般化的,并且不会发生短路以允许计划重用。