昨天我试图对过滤索引进行一些测试,并创建了一个相当简单的情况:

CREATE TABLE IndexTest 
(
    ID INT NOT NULL IDENTITY(1,1) CONSTRAINT pk_IndexTest PRIMARY KEY, 
    Col1 CHAR(1)
)
GO

CREATE INDEX ix_IndexTest ON IndexTest(Col1)
WHERE Col1 IS NOT NULL
GO

INSERT INTO IndexTest VALUES ('A'),('B'),('C'),('D')
INSERT INTO IndexTest VALUES (''),(''),(''),('')
INSERT INTO IndexTest VALUES (NULL),(NULL),(NULL),(NULL)
GO

然后,我尝试使用提示运行以下查询,以强制它使用过滤索引。

SELECT * 
FROM IndexTest WITH (INDEX(ix_IndexTest))
GO

但我不断收到错误:

由于此查询中定义的提示,查询处理器无法生成查询计划。重新提交查询,不指定任何提示,也不使用 SET FORCEPLAN。

如果我移除过滤器但不连同它,它会起作用。我什至尝试以各种方式更改过滤器,例如 WHERE Col1 = '' ETC。每当我有过滤器时,我都会收到该错误。谁能告诉我为什么?难道我做错了什么?

我在 SQL Server 2012 和 2014 实例上尝试了此操作,并在此处创建了它的 sqlfiddle:

http://www.sqlfiddle.com/#!6/4a850/1

编辑: 不确定这是否重要,但具体而言,我想看看是否可以看到索引实际包含哪些行。这个想法来自于看到有人使用这种技术从损坏的表中恢复数据(使用未过滤的索引)。

有帮助吗?

解决方案

可能的脱节是过滤索引不会自动为您过滤结果 - 您必须编写一个与索引的过滤谓词匹配的 WHERE 子句才能使用该索引。

换句话说,如果您只想获取行 WHERE Col1 IS NOT NULL, ,你还需要一个 WHERE 子句来限制行。如果 SQL Server 无法使用您试图强制满足查询返回的所有行的索引,则通过定义包含比表更少(或相同*)行的索引来做到这一点,它无法运行。

* 可能有些情况我还没有测试过,这是否可行;例如,如果该列被限制为 NOT NULL (或者所有行恰好与过滤谓词匹配),那么过滤索引将通过扩展表示整个表中的行。但这是一个非常人为且不切实际的场景(即使它有效)。SQL Server 无法针对所有行当前恰好与过滤器匹配的情况生成计划,因为单个插入或更新可能会破坏它。

如果您尝试从非聚集索引中查找数据,因为基础表/CI 已损坏,那么您可能会有更好的运气 DBCC IND/PAGE 或者模仿使用十六进制编辑器的 Paul Randal。

许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top