2

我有以下查询

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,问题仍然存在

4

2 回答 2

1

SQL 使计划可重用于变量。

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

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

作为:

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

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

于 2017-11-20T15:54:35.973 回答
0

这个查询有变量,这个问题中关于 sql server 的建议不知道你的变量的值,因此必须根据结果集的猜测大小制定一个计划,这与你的问题有关。

为什么当 WHERE 子句包含参数化值时 SQL Server 使用索引扫描而不是索引查找

但是,您提到要将此代码转换为存储过程。在将其转换为存储过程时,查询优化器应该能够嗅探变量的值并从中开发和执行计划。尝试将其转换为存储过程并执行它。在这些条件下,查询计划应该会有所改进。

于 2017-11-20T14:57:34.260 回答