小编典典

引起聚集索引扫描的日期参数

sql

我有以下查询

DECLARE @StartDate DATE = '2017-09-22'
DECLARE @EndDate DATE = '2017-09-23'

SELECT a.col1,
       a.col2,
       b.col1,
       b.col2,
       b.col3,
       a.col3
FROM   TableA a
       JOIN TableB b
           ON b.pred = a.pred
WHERE  b.col2 > @StartDate AND b.col2 < @EndDate

当我运行它并检查实际的执行计划时,我可以看到最昂贵的运算符是聚集索引扫描(索引位于a.pred上)

但是,如果我按以下方式更改查询

SELECT a.col1,
       a.col2,
       b.col1,
       b.col2,
       b.col3,
       a.col3
FROM   TableA a
       JOIN TableB b
           ON b.pred = a.pred
WHERE  b.col2 > '2017-09-22' AND b.col2 < '2017-09-23'

消除了索引扫描,并使用了索引查找。

有人可以解释为什么吗?在我看来,这与以下事实有关:变量中的值可以是任何值,因此SQL不知道如何计划执行。

有什么办法可以消除表扫描但仍然可以使用变量?(PS,它将转换为以@StartDate和@EndDate作为参数的存储过程)

编辑

col2是DATETIME,但是,如果我将变量设为DATETIME,问题仍然存在


阅读 293

收藏
2021-05-30

共1个答案

小编典典

SQL使计划可重用于变量。

当您使用变量时-它会编译查询而不会知道您将传递的实际值。即使在此sql batch值也是已知的。 但是 它不需要为另一组传递参数重新编译查询。

因此,如果您对值进行硬编码-
DB将对其进行编译,以选择针对这些特定值优化的计划(例如,它猜测通过日期检查的预期行数)。这比使用变量“至少不会更糟”。但是DB需要为另一组硬编码的值重新编译它(因为查询的文本已更改),这需要时间,并且垃圾compiled query cache存储量会取代其他有用的查询。

作为:

有什么办法可以消除表扫描但仍然可以使用变量?(PS,它将转换为以@StartDate和@EndDate作为参数的存储过程)

我认为非聚集索引b.col2可能是解决方案。此索引的键还可以包含b.pred作为代理键的一部分或包含(with include(pred))。

2021-05-30