1

我有一张桌子,我们就叫它吧History。主键(又名聚集索引)称为HIST_ID. 该表在开发数据库中有大约 2300 行。现在考虑以下两个查询:

查询一:

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最后。但是,第一个查询执行索引查找,第二个查询执行索引扫描。是什么赋予了?

先发制人的答复 - 不,选项(重新编译)没有帮助。

补充:我想要一些有说服力的事实,而不是猜测。我自己可以猜出十几个可能的原因。但这里真正的问题是什么?

4

3 回答 3

1

我建议将计划与传入/使用的参数分开生成,因此本质上需要(取决于@x 的值)返回每一行。因此,查询计划正在处理它可以接收的参数的最坏情况。

例如,如果@x 的输入为空,则查询将被迫返回每一行,因为每一行都满足始终返回true 的文字等式/谓词。为了让查询计划覆盖@x 的每个值,它必须生成一个执行扫描的计划。

于 2009-10-23T14:32:33.477 回答
0

我猜优化器确定它是有益的。另一种方法是使用与您编写的相同的计划

select * from History where hist_id=@x
union all
select * from History where @x is null

您可以以这种方式重写查询,但我很确定优化器能够自行完成此操作。你有多少个空值?

编辑:原来我误读了这个问题,并认为你想要 WHERE(@x = hist_id OR hist_id 为空)。事实上,您需要一个动态标准。看看这篇文章。请注意,如果您指定 WITH(RECOMPILE),您的这种查询应该可以在 SQL2k8 中工作,但是由于一个讨厌的错误,这种支持被删除了。

于 2009-10-23T14:26:41.943 回答
0

当然是索引扫描。

聚集索引扫描 = 表扫描,因为“@x IS NULL”没有合理的谓词。

参数化的缓存计划是通用的,适用于@x = NULL 或@x = value。如果你不定义@x,你应该得到相同的计划。

如果您编码了“12345 IS NULL”,则会检测到并忽略它。

我找不到关于如何在查询计划中处理常量的博客文章。要点是它们是通用的,并且不会发生短路以允许计划重用。

于 2009-10-23T14:33:53.600 回答