为什么这个过滤索引不仅没有使用,而且在我尝试强制它时出错

Ken*_*her 4 sql-server-2008-r2 filtered-index

我正在尝试调整一个相当烦人的查询,其中许多表看起来像:

date = '9999-12-31 23:59:59.9999999'
Run Code Online (Sandbox Code Playgroud)

至少有几个表在 300 百万行范围内,当我过滤它时,我最终在 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
Run Code Online (Sandbox Code Playgroud)

但是,当我检查查询计划时,它不会使用索引。它只是进行了聚集索引扫描和键查找。显然没有任何数据这是有道理的,但即使有我的所有数据,它也做同样的事情。我没有费心提供任何数据的原因是无论如何都会发生的第二个问题。

当我试图强制索引查看查询计划的样子时:

SELECT col2,col3 FROM test 
WITH (index (ix_filtered))
WHERE col4 = '9999-12-31 23:59:59.9999999'
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

消息 8622,级别 16,状态 1,第 52 行 由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。

我试图弄清楚为什么我收到查询提示错误。我的猜测是,这个问题的答案也会告诉我为什么根本没有使用过滤索引。

Ken*_*tle 6

我们发现发生这个错误是因为在数据库级别开启了强制参数化。对于想要重现它的其他读者,请在数据库中使用 Kenneth 的示例代码,并启用此设置:

create database filterme;
GO

alter database filterme set parameterization forced;
GO
Run Code Online (Sandbox Code Playgroud)

对于参数化查询,SQL Server 不想缓存带有可能不适用于其他参数值的筛选索引的执行计划。

在 SQL Server 的更高版本中,“非提示”计划的查询计划(希望)会显示“不匹配的索引”警告,以更好地提醒您注意这一点。这在 SQL Server 2008R2 中不存在。(注意:我刚刚在 SQL 2016 上进行了测试,但没有看到不匹配的索引警告,需要进一步研究为什么它没有出现在此示例代码中。)

要在不更改数据库设置的情况下使查询工作,您可以在查询中使用“选项重新编译”提示。这是有效的,因为它指示 SQL Server 不要缓存计划以供重用。