我有以下查询
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,问题仍然存在
SQL使计划可重用于变量。
当您使用变量时-它会编译查询而不会知道您将传递的实际值。即使在此sql batch值也是已知的。 但是 它不需要为另一组传递参数重新编译查询。
sql batch
因此,如果您对值进行硬编码- DB将对其进行编译,以选择针对这些特定值优化的计划(例如,它猜测通过日期检查的预期行数)。这比使用变量“至少不会更糟”。但是DB需要为另一组硬编码的值重新编译它(因为查询的文本已更改),这需要时间,并且垃圾compiled query cache存储量会取代其他有用的查询。
compiled query cache
作为:
我认为非聚集索引b.col2可能是解决方案。此索引的键还可以包含b.pred作为代理键的一部分或包含(with include(pred))。
b.col2
with include(pred)