SQL Server不支持在视图上创建过滤索引,筛选索引仅适用于基础表且WHERE条件需确定、单表、列需在索引中;索引视图可含WHERE但需SCHEMABINDING和唯一聚集索引,并依赖NOEXPAND提示才能命中。

CREATE INDEX 不能用于视图,SQL Server 不支持在视图上直接创建带 WHERE 条件的“过滤索引”——你真正能用的,是「筛选索引(Filtered Index)」,但它只作用于基础表,不是视图。
如果你看到“过滤条件的索引视图”这种说法,大概率是混淆了两个独立特性:INDEXED VIEW(即带唯一聚集索引的视图)和 FILTERED INDEX(表级带 WHERE 的非聚集索引)。它们不能叠加使用。
FILTERED INDEX 必须建在基础表上,且 WHERE 条件有硬性限制
筛选索引本质是表上的非聚集索引,只是加了谓词。它的 WHERE 子句不是任意写的,必须满足:
- 表达式必须是确定性的(比如不能含
GETDATE()、NEWID()) - 不能引用视图、函数(除少数内置标量函数如
ISNULL、CASE)、子查询 - 所有列必须来自同一张表,不能跨表或涉及计算列(除非该列是持久化且确定的)
-
WHERE中的列最好已经出现在索引键或包含列中,否则优化器可能无法匹配查询
例如,订单表里 95% 订单状态为 'completed',只查未完成的:
CREATE NONCLUSTERED INDEX IX_orders_pending
ON orders (customer_id)
WHERE status IN ('pending', 'processing');这个索引生效的前提是:你的查询 WHERE status IN ('pending', 'processing') 必须与索引谓词逻辑等价或更严格。如果写成 WHERE status = 'pending',依然能命中;但若写成 WHERE status != 'completed',就不会命中——优化器无法推导逻辑等价性。
INDEXED VIEW 本身不支持 WHERE 过滤,但可间接实现类似效果
你可以创建一个带 WHERE 的视图,再给它加唯一聚集索引,从而固化结果集。但注意:
- 视图必须是 SCHEMABINDING
- 必须有 唯一聚集索引(这是“索引视图”成立的前提)
- 视图定义中允许
WHERE,但该过滤仅影响视图内容,不改变底层索引结构 - 查询要命中该索引视图,必须精确匹配视图定义中的表名、列名、连接方式和过滤条件,或者启用
SET NOCOUNT ON+QUERYTRACEON 8601等高级选项(不推荐生产环境依赖)
典型例子:
CREATE VIEW dbo.v_pending_orders
WITH SCHEMABINDING
AS
SELECT id, customer_id, order_date
FROM dbo.orders
WHERE status IN ('pending', 'processing');CREATE UNIQUE CLUSTERED INDEX PK_v_pending_orders ON dbo.v_pending_orders (id);
此时,只有当查询写成:
SELECT id, customer_id FROM dbo.v_pending_orders WHERE id = 123;
才可能直接走这个聚集索引。如果直接查 orders 表并加同样 WHERE,SQL Server 默认不会自动重写为走该视图——除非你显式 WITH (NOEXPAND) 提示:
SELECT * FROM dbo.v_pending_orders WITH (NOEXPAND) WHERE id = 123;
容易被忽略的关键点:统计信息不自动适配过滤范围
筛选索引自带独立的统计信息,但它的统计对象仅限于满足 WHERE 条件的那部分数据行。这意味着:
- 如果业务逻辑变化(比如原本只查
'pending',现在新增'reviewing'状态),旧的筛选索引就失效了 -
UPDATE STATISTICS必须显式指定索引名,否则默认只更新全表统计信息:
UPDATE STATISTICS orders IX_orders_pending;
- 若查询参数化后导致谓词无法静态匹配(比如
WHERE status = @status),即使@status实际值落在筛选范围内,也可能因参数嗅探或统计偏差而跳过该索引
真正决定“命中率”的,从来不是语法多漂亮,而是查询谓词能否被优化器静态识别为与筛选索引谓词兼容——这要求开发时就对查询模式和数据分布有明确约束。

















