SQL Server 2019 中唯一可落地的“物化视图”是索引视图,必须先用 WITH SCHEMABINDING 创建并满足所有硬性条件(如两段式表名、COUNT_BIG、无*或TOP等),再创建唯一聚集索引才能持久化数据;否则仍为普通虚拟视图。

SQL Server 2019 中没有叫“物化视图”的原生对象,真正能落地的只有 索引视图(Indexed View),它才是唯一能把聚合结果持久化到磁盘、从而加速重复查询的机制。
为什么不能直接建“物化视图”?
SQL Server 不支持 PostgreSQL 那种 MATERIALIZED VIEW 语法,也没有 Oracle 那类自动刷新的物化视图。所谓“物化”,在 SQL Server 里只体现为:给一个带 SCHEMABINDING 的视图加上 UNIQUE CLUSTERED INDEX,此时 SQL Server 才会把视图结果物理存储下来。
- 没加聚集索引的视图,每次查询都是重跑底层 SQL,和写个 SELECT 语句没区别
- 即使你给基础表建了
(region, sales)组合索引,SELECT SUM(sales) FROM orders GROUP BY region依然可能走全表扫描——优化器不会自动下推聚合到普通索引上 -
CREATE VIEW ... AS SELECT region, SUM(sales) ... GROUP BY region这条语句本身不物化任何数据,只是存了个定义
创建索引视图必须满足的硬性条件
缺一不可,否则 CREATE UNIQUE CLUSTERED INDEX 会直接报错:
- 视图必须用
WITH SCHEMABINDING创建,且所有引用的表都用两段式名(如dbo.orders) - 不能出现
*、TOP、ORDER BY、GETDATE()、子查询、OUTER JOIN - 聚合必须用
COUNT_BIG(*)替代COUNT(*)(这是确定性要求) - 基表要有主键或唯一非空约束,且聚集索引键必须覆盖视图中所有分组列和连接列
- 会话级 SET 选项必须全部开启:
ANSI_NULLS=ON、QUOTED_IDENTIFIER=ON、ANSI_WARNINGS=ON、ARITHABORT=ON、CONCAT_NULL_YIELDS_NULL=ON、NUMERIC_ROUNDABORT=OFF
如何让查询真正命中索引视图?
建完不等于生效。常见现象是执行计划里仍显示“聚集索引扫描”基础表,而不是读取视图的索引:
- 查询必须显式引用视图名,比如
SELECT * FROM dbo.vw_SalesByRegion;写成SELECT region, SUM(sales) FROM orders GROUP BY region就完全不触发 - 若视图定义是
GROUP BY region,但查询写了WHERE region LIKE 'N%',优化器可能因估算不准而放弃使用索引视图 - 参数化查询容易导致计划缓存失效或隐式转换,建议搭配
WITH (NOEXPAND)提示强制走视图索引:SELECT * FROM dbo.vw_SalesByRegion WITH (NOEXPAND) WHERE region = 'North' - 跨数据库查询、用了
OPTION(RECOMPILE)或某些 hint,也可能绕过索引视图
索引视图不是银弹,得盯住刷新和维护成本
它本质是一张由系统维护的只读表,基表每行 INSERT/UPDATE/DELETE,都会同步更新索引视图的物化数据:
- 高并发写入场景下,索引视图会成为瓶颈——它增加 DML 开销,还可能引发死锁
- 视图越大、索引越多,每次修改带来的日志增长和锁持有时间越长
- 不要以为建了索引就万事大吉:物化数据不会自动重建统计信息,需定期手动更新
UPDATE STATISTICS dbo.vw_SalesByRegion - 若基表每天批量更新一次,而报表查询集中在白天,那索引视图非常合适;但若订单表每秒写入数百行,就得慎重评估是否值得
最常被忽略的一点:索引视图一旦创建,基表结构就不能随意改——删列、改类型、去主键都会失败,因为 SCHEMABINDING 锁死了依赖关系。上线前务必确认业务变更节奏能否接受这种约束。










