使用覆盖WHERE子句在筛选索引上使用哪些键列?

时间:2014-03-12 16:21:12

标签: sql sql-server sqlperformance filtered-index

我正在创建一个过滤索引,以便WHERE过滤器包含完整的查询条件。对于这样的索引,似乎不需要键列,尽管SQL要求我添加一个。例如,请考虑表:

CREATE TABLE Invoice
(
    Id INT NOT NULL IDENTITY PRIMARY KEY,
    Data VARCHAR(MAX) NOT NULL,
    IsProcessed BIT NOT NULL DEFAULT 0,
    IsInvalidated BIT NOT NULL DEFAULT 0
)

表格上的查询查找要处理的新发票,即:

SELECT *
FROM Invoice
WHERE IsProcessed = 0 AND IsInvalidated = 0

因此,我可以使用过滤索引调整这些查询:

CREATE INDEX IX_Invoice_IsProcessed_IsInvalidated
ON Invoice (IsProcessed)
WHERE (IsProcessed = 0 AND IsInvalidated = 0)
GO

我的问题:IX_Invoice_IsProcessed_IsInvalidated的关键列应该是什么?据推测,关键列未被使用。我的直觉导致我选择一个小的列,并保持索引结构相对平坦。我应该选择表主键(Id)吗?其中一个过滤列,或两者都是?

3 个答案:

答案 0 :(得分:1)

因为你在该表上有一个聚集索引,所以你放在该索引的键列中的内容并不重要;意思Id是免费的。您可以做的唯一事情是include索引的包含部分中的所有内容,实际上在索引的叶级别实际拥有数据,以排除对表的键查找。或者,如果队列很大,那么,或许,其他一些列在关键部分会很有用。

现在,如果该表没有主键,那么您必须include或指定加入或其他目的所需的所有列作为键列。否则,将发生堆上的RID查找,因为在索引的叶级别上,您将引用数据页。

答案 1 :(得分:0)

此过滤索引覆盖的表的百分比是多少?如果它很小,你可能想覆盖整个表来处理" SELECT *"从索引没有击中表。如果它是表格的一大部分,虽然这不是最佳的。然后我建议使用聚簇索引或主键。我不得不进行更多的研究,因为我忘了现在哪个是最优的,但如果它们相同则应该设置。

答案 2 :(得分:0)

我建议您声明如下

CREATE INDEX IX_Invoice_IsProcessed_IsInvalidated
ON Invoice (Id)
INCLUDE (Data)
WHERE (IsProcessed = 0 AND IsInvalidated = 0)

INCLUDE子句意味着数据列的值将存储为索引的一部分。

如果您没有INCLUDE子句,那么

的查询计划
SELECT Id, Data
FROM Invoice
WHERE IsProcessed = 0 AND IsInvalidated = 0

将涉及两个步骤

  • 使用索引查找与之匹配的主键值列表 标准
  • 从表中获取与这些主键匹配的数据

另一方面,如果索引包含[Data]列,那么它将正确覆盖查询,因为不需要使用主键查找数据

尽管

,你什么也得不到

这样做的缺点是,您将为这些记录存储两次varchar(MAX)数据,因此需要将更多数据写入数据库,并且将使用更多存储空间,尽管这不是很多如果你只是在讨论一小部分数据,那么就会出现问题。

与往常一样,你把更多的时间和精力放在一边小心翼翼地把东西放回去。