视图中使用SELECT *会破坏覆盖索引,因其导致优化器无法确认实际查询字段,从而放弃覆盖判断、强制回表;必须显式指定字段、控制嵌套层数、避免子查询阻断下推,并在SQL Server中启用SCHEMABINDING方可支持索引视图。

视图里SELECT * 直接废掉覆盖索引
覆盖索引生效的前提是:查询涉及的所有字段(SELECT列 + WHERE/ON条件列)都落在同一个联合索引里。但视图定义中若写了SELECT *,哪怕底层表有完美索引,优化器也无法确认哪些列会被实际用到,于是放弃覆盖判断,强制回表。
常见错误现象:EXPLAIN显示Extra列是Using where或Using index condition,而不是Using index。
- 必须显式列出所需字段,例如把
SELECT *改成SELECT user_id, name, status - 如果视图被多处调用且字段需求不一,不要试图“一视图打天下”,拆成多个窄口径视图更可控
- MySQL 5.7 及以前版本对视图字段推导能力极弱,
SELECT *几乎必然导致全表扫描
视图嵌套三层以上,覆盖索引基本失效
SQL Server 和 PostgreSQL 虽支持视图合并(view merging),但一旦嵌套超过三层,优化器常放弃代价估算,转而采用保守策略——先物化中间结果,再执行外层逻辑。此时即使最内层查询本可走覆盖索引,也会因物化过程丢失索引上下文。
典型表现:EXPLAIN FORMAT=TREE里出现MATERIALIZE节点,或rows值突然暴涨数个数量级。
- 优先用
JOIN替代多层SELECT FROM (SELECT ...)结构 - 若必须嵌套,控制在两层以内,并确保每层都显式声明字段名(禁用
SELECT *) - SQL Server 中可尝试加
OPTION (RECOMPILE)提示,促使优化器重估执行路径
视图中用了子查询,覆盖索引只看外层
子查询是独立执行单元,它的字段是否被索引、是否走覆盖,完全不影响外层查询的索引选择。很多开发者误以为“子查询只查了索引字段,主查询就能省事”,其实优化器对外层SELECT仍按原始逻辑评估——只要外层要的字段没全在索引里,就一定回表。
例如:SELECT id, name FROM orders WHERE id IN (SELECT order_id FROM refunds WHERE amount > 100),即使orders表上有(id, name)索引,只要refunds子查询没被下推为JOIN,外层仍可能走全表扫描。
- 把
IN/EXISTS子查询改写为LEFT JOIN或INNER JOIN,并把子查询条件挪进ON - 为
JOIN涉及的字段建联合索引,例如refunds(order_id, amount) - 避免在视图定义里写
WHERE col IN (SELECT ...)这类结构,它天然阻断条件下推
SQL Server 视图没加 SCHEMABINDING,索引根本不会被考虑
在 SQL Server 中,普通视图无法绑定索引;只有用WITH SCHEMABINDING创建的视图,才具备创建唯一聚集索引的前提。没加这句,后续所有索引操作都会报错Cannot create index on view 'xxx' because the view is not schema bound。
更隐蔽的问题是:即使你建好了索引视图,查询也不自动走它——优化器默认仍展开原逻辑,除非查询严格匹配索引视图的列和过滤条件。
- 建视图时必须用两段式名称,如
dbo.Orders,不能用Orders -
SELECT列表不能含*、GETDATE()、TOP、聚合函数(除非配COUNT_BIG(*)) - 查询中引用该视图时,
SELECT字段必须是索引键的前缀子集,且WHERE条件需覆盖索引首列
真正卡住性能的,往往不是索引建得不够多,而是视图结构让优化器“不敢信”——字段模糊、嵌套过深、子查询隔离、绑定缺失。这些地方不先理清,加再多索引也只是堆在那儿。

















