我正在尝试调整一个相当烦人的查询,其中许多表的内容如下所示:
date = '9999-12-31 23:59:59.9999999'
至少有几个表在 3 亿行范围内,当我过滤它时,我最终在 2 百万行范围内。尝试过滤索引似乎是合理的。
CREATE TABLE test (col1 int PRIMARY KEY, col2 int, col3 varchar(50), col4 datetime2(7), col5 int);
CREATE INDEX ix_filtered ON test(col2,col3) INCLUDE (col4)
WHERE col4 = '9999-12-31 23:59:59.9999999' ;
GO
SELECT col2,col3 FROM test
WHERE col4 = '9999-12-31 23:59:59.9999999';
GO
但是,当我检查查询计划时,它不会使用索引。它只是进行了聚簇索引扫描和键查找。显然,在没有任何数据的情况下,这是有道理的,但即使使用我所有的数据,它也做了同样的事情。我懒得提供任何数据的原因是无论如何都会出现第二个问题。
当我试图强制索引查看查询计划时:
SELECT col2,col3 FROM test
WITH (index (ix_filtered))
WHERE col4 = '9999-12-31 23:59:59.9999999'
我收到此错误:
由于此查询中定义的提示,消息 8622、级别 16、状态 1、第 52 行查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。
我试图弄清楚为什么我会收到查询提示错误。我的猜测是,这个问题的答案也会告诉我为什么过滤索引根本没有被使用。
我们发现发生此错误是因为在数据库级别启用了强制参数化。对于其他想要重现它的读者,请在数据库中使用 Kenneth 的示例代码并启用此设置:
对于参数化查询,SQL Server 不希望缓存具有可能不适用于其他参数值的过滤索引的执行计划。
在更高版本的 SQL Server 中,“非提示”计划的查询计划(希望)会显示“不匹配的索引”警告,以更好地提醒您注意这一点。SQL Server 2008R2 中不存在这种情况。(注意:我刚刚在 SQL 2016 上进行了测试,但没有看到不匹配的索引警告,需要更多地研究为什么它没有出现在这个示例代码中。)
要在不更改数据库设置的情况下使查询工作,您可以在查询中使用“选项重新编译”提示。这是可行的,因为它指示 SQL Server 不缓存计划以供重用。