sql - 导致聚集索引扫描的日期参数

标签 sql sql-server sql-execution-plan query-tuning

我有以下查询

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 批处理 中,值也是已知的。 但是它不需要为另一组传递参数重新编译查询。

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

截至:

Is there any way I can eliminate the table scan but still have use of the variable? (PS, this will be converted to a stored procedure which takes @StartDate and @EndDate as parameters)

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

关于sql - 导致聚集索引扫描的日期参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46852874/

相关文章:

sql - 按日期范围消除行

php - 连接表上的 ORDER BY

sql - 这两个舍入表达式的值不同吗?

sql - 针对日期范围的条件的查询性能

PHP SQL 查询检查是否包含 INSERT

sql - 如何从 PostgreSQL 中的另一个表插入对 UUID 的引用?

.net - 如何将故障转移伙伴添加到 VB.NET 中的连接字符串

c# - Entity Framework 为映射到 View 的实体生成的 SQL

sqlite3 一起选择最小值,最大值比单独选择它们要慢得多

oracle-sqldeveloper - 在 SQL Developer 中使用解释计划时出现 "Commit prior transaction"