视图每次调用都触发完整重解析,因其仅为保存的select文本,非预编译对象;需重新解析、绑定、生成执行计划,子查询不缓存、计划不复用,三层以上嵌套易致优化器退化、条件下推失效、索引无法下推。

视图每次调用都触发完整重解析
视图不是预编译对象,它只是保存的一段 SELECT 文本。每次执行 SELECT * FROM v_user_stats,数据库都要把整个视图定义(含所有子查询)内联展开,再走一遍语法解析、语义绑定、统计信息读取和执行计划生成流程。这意味着:子查询逻辑不会被缓存,也不会复用计划——哪怕你一秒钟调用十次相同视图,它就重解析十次。
- 相关子查询(如引用外层表字段的
(SELECT MAX(o.amount) FROM orders o WHERE o.user_id = u.id))会让优化器无法提前固化执行路径,必须保留嵌套结构反复校验 - 三层以上嵌套时,MySQL 的
optimizer_search_depth可能触发“超时退化”,跳过代价估算直接选次优计划 - SQL Server 在高并发下容易因解析树膨胀导致 CPU 卡在 parser 阶段,
sys.dm_exec_requests中常看到status = running但wait_type = SOS_SCHEDULER_YIELD
子查询破坏索引下推(ICP)和条件下推
MySQL 的索引下推(ICP)要求 WHERE 条件能安全地下推到 InnoDB 引擎层过滤。但一旦条件出现在子查询视图中(比如 CREATE VIEW v_active AS SELECT * FROM (SELECT * FROM user WHERE status = 1) t),优化器就无法确认该条件是否仍作用于原始索引字段——因为内层结果可能已被物化为派生表,原始索引结构已不可见。
- EXPLAIN 中
Extra字段从Using index condition退化为Using where,联合索引如(name, age)对age > 25的过滤失效 - 外层加
WHERE age > 30,实际执行是先回表取全行,再由 Server 层过滤,I/O 暴增 - 视图含
GROUP BY、DISTINCT或窗口函数时,ICP 几乎必然禁用
UNION ALL 或 NOT IN 类子查询强制物化中间结果
视图里用 UNION ALL 或 NOT IN,数据库通常不会跨子句做全局优化。每个分支都得独立执行,且结果必须合并后才能继续处理——这极易触发临时表和磁盘排序,尤其外层再加 ORDER BY 或 LIMIT 时。
-
NOT IN子查询会被强制物化成临时结构,哪怕只返回几行;而NOT EXISTS可走关联执行,常被优化为Hash Anti Join -
UNION ALL各子查询若未加WHERE过滤,或类型不一致(如VARCHAR(50)vsVARCHAR(255)),会引发隐式转换和索引失效 - MySQL 默认用
TEMPTABLE算法处理含子查询的视图,比MERGE算法慢一个数量级(可用SHOW CREATE VIEW查算法)
嵌套层级加深导致优化器放弃下推与穿透
三层以上视图嵌套时,外层 WHERE 条件几乎无法“穿透”到最底层基表。每层视图都是独立逻辑封装,优化器在展开时会保守处理上下文绑定——你写的 WHERE user_id = 123 很可能只作用于最终结果集,而非驱动底层索引扫描。
- PostgreSQL 和 SQL Server 中,
sp_depends已弃用,pg_get_viewdef()展开的等价 SQL 执行计划常与原视图查询不一致 - MySQL 5.7+ 默认对含子查询的视图启用
TEMPTABLE,外层谓词根本没机会参与子查询的谓词下推 - 标量子查询(放在
SELECT列里的子查询)会让ORDER BY、LIMIT失效:排序只能发生在全部子查询计算完成后,无法利用内部索引顺序
关键点在于:子查询本身不是性能杀手,但把它塞进视图后,就失去了对外部查询条件的响应能力——你没法控制它何时执行、是否物化、是否走索引。真正可控的,永远是主查询直接面对基表的写法。










