视图合并是优化器将视图定义与外层查询合并为单条sql以复用底层表索引的技术;成功合并时explain的table列显示物理表名,否则显示或subquery scan表明物化。

视图查询没走全表扫描,说明优化器做了视图合并
视图本身不存储数据,只是保存的 SELECT 语句。当外部查询引用视图时,MySQL 或 PostgreSQL 的优化器**可能**把视图定义和外层查询“拼”成一条新 SQL,再做执行计划分析——这个过程叫「视图合并(view merging)」。一旦合并成功,就能复用底层表的索引,自然避免全表扫描。
但不是所有视图都能被合并。常见阻断点包括:
-
DISTINCT、GROUP BY、UNION、窗口函数等逻辑会强制物化视图结果,无法下推条件 - 视图里用了
ORDER BY(没配LIMIT)或FOR UPDATE - 外层查询的
WHERE条件字段不在视图 SELECT 列表中(比如视图只选了id, name,但外层加了WHERE status = 1) - MySQL 中视图定义含子查询且不可展开(如相关子查询)
怎么确认是否发生了视图合并?看 EXPLAIN 输出的 table 列
执行 EXPLAIN SELECT * FROM my_view WHERE id = 123,重点观察 table 列:
- 如果显示的是底层物理表名(如
orders、users),说明已合并,优化器直接在原表上走索引 - 如果显示
<derivedn></derivedn>(MySQL)或Subquery Scan(PostgreSQL),说明视图被物化成临时结果集,后续过滤只能在临时集上做,极易触发全表扫描 -
key列为NULL且rows很大,基本可判定未合并或合并后条件未下推
哪些写法会让视图必然无法合并?
以下结构会关闭合并路径,即使底层表有索引也大概率白搭:
- 视图定义里带
CREATE VIEW v AS SELECT * FROM t WHERE col IS NOT NULL——IS NOT NULL在 MySQL 8.0.22+ 才支持下推,旧版本直接物化 -
CREATE VIEW v AS SELECT id, UPPER(name) AS name_upper FROM users—— 函数列导致无法将外层WHERE name_upper = 'ABC'下推到原表 -
CREATE VIEW v AS SELECT a.id, b.name FROM orders a JOIN users b ON a.user_id = b.id,外层加WHERE b.status = 1—— 若status不在视图 SELECT 列表中,条件无法下推到users表 - PostgreSQL 中视图含 CTE(
WITH子句),默认不合并;需显式用MATERIALIZED或NOT MATERIALIZED控制
想强制合并或绕过物化,有什么实际手段?
没有通用“开关”,但可通过改写降低物化概率:
- MySQL:避免在视图里写
SELECT *,明确列出需要的字段,尤其确保外层要用的过滤字段在列表中 - 把视图里冗余的
ORDER BY全部删掉(除非真需要排序结果) - 用内联表(
SELECT ... FROM (SELECT ...) AS v)替代命名视图,部分场景更易被优化器内联 - PostgreSQL 可尝试在查询前加
SET enable_material = off(仅调试用,生产禁用),观察是否从Subquery Scan变成直接访问基表 - 最稳妥的方式:别依赖视图隐藏逻辑,把关键查询逻辑直接写在外层,用注释标明复用意图
视图合并是优化器的内部决策,不暴露控制接口。真正容易被忽略的是:你以为加了索引就万事大吉,但只要视图定义里有一处函数或缺失字段,整个链路就退回物化模式——这时候看 EXPLAIN 的 table 列比背原理管用得多。










