索引视图本质是物化视图,需严格满足SCHEMABINDING、SET选项(ANSI_NULLS等必须ON)、唯一聚集索引键确定性等条件;否则无法创建或退化为普通视图,且会显著拖慢写入性能。

索引视图不是“给视图加个索引”那么简单,它本质是物化视图——数据被物理存储,查询优化器能自动重写查询来命中它,但前提是所有条件都严格满足。否则,它就退化成普通视图,甚至拖慢写入。
为什么CREATE INDEX失败总报“ANSI_NULLS must be ON”
这是创建索引视图最常卡住的第一步。错误信息 Cannot create index on view 'xxx' because the view is not schema-bound 或更隐蔽的 SET options do not meet requirements,往往不是因为没加 WITH SCHEMABINDING,而是会话级 SET 选项不匹配。
-
ANSI_NULLS、ANSI_PADDING、QUOTED_IDENTIFIER必须为ON;ANSI_WARNINGS和ARITHABORT也建议设为ON - 这些设置必须在
CREATE VIEW语句执行前就生效,不是在 SSMS GUI 里点一下就行——得用SET ANSI_NULLS ON;显式声明,再执行建视图语句 - 如果基表是别人建的,且当初建表时没设对 SET 选项(比如用 ODBC 默认连接建的),那即使你当前会话设对了,建索引视图仍会失败
WITH SCHEMABINDING 不只是语法要求,它锁死依赖链
WITH SCHEMABINDING 是强制项,但它带来的约束远超文档描述:一旦加上,你就不能随意删改或重命名视图里引用的任何列、表、函数,哪怕只是改个列名也会报错 The object 'xxx' is dependent on column 'yyy'。
- 视图中所有对象(表、列、函数)必须带两段式名称,如
dbo.Orders,不能只写Orders - 不能用
SELECT *,所有列必须显式列出,且不能含计算列(除非是确定性表达式) - 若视图引用了标量函数,该函数也必须用
WITH SCHEMABINDING创建,否则绑定失败 - 后续要修改基表结构?先 DROP 视图,改完再重建——生产环境务必评估停机窗口
唯一聚集索引必须覆盖所有行,且键不能含NULL
索引视图的第一个索引必须是 UNIQUE CLUSTERED,而且这个唯一键必须能区分每一行结果。很多人试图用 ROW_NUMBER() OVER(...) 或 NEWID() 来凑唯一性,这是错的——这些函数是非确定性的,SQL Server 直接拒绝创建。
- 唯一键应来自基表的自然候选键组合,例如
Orders.OrderID, OrderDetails.LineID - 如果源数据本身存在重复行(比如 LEFT JOIN 导致主表一行对应多行明细),必须用
DISTINCT或聚合(GROUP BY)预处理,否则建唯一索引会因重复键失败 - 键列不允许为 NULL;若某列可能为空,需用
ISNULL(col, -1)等确定性替换,但要注意业务语义是否允许 - 建好后,检查
sys.indexes中is_hypothetical = 0且type_desc = 'CLUSTERED',确认真被物化
写入性能下降比想象中更严重,尤其在高并发更新场景
索引视图不是只读缓存。每当基表发生 INSERT/UPDATE/DELETE,SQL Server 必须同步维护所有依赖它的索引视图。一个被 5 个索引视图引用的订单表,在更新单条记录时,可能触发 5 次额外的索引更新+日志写入。
- 用
sys.dm_db_index_usage_stats查看user_updates,对比普通索引和索引视图的更新频次,差距常达 3–10 倍 - 批量导入数据前,临时
DROP索引视图,导入完成再重建,比硬扛同步开销更实际 - 如果报表查询频率远低于写入频率(比如每分钟写入千条,每天只查几次),索引视图大概率得不偿失
- 测试时别只看 SELECT 性能,务必压测真实 DML 路径——
UPDATE Orders SET Status = 'Shipped' WHERE OrderDate 这类语句最容易暴露问题
真正难的不是语法通过,而是判断“这个报表是否值得用索引视图”。它适合那些查询极其固定、数据变更不频繁、且执行计划长期无法靠普通索引优化的重型聚合报表。一旦选错,修复成本远高于一开始不用。










