视图中使用 select * 会导致语义锁死,破坏过滤与投影下推,引发全字段扫描、物化、排序失效及嵌套穿透失败等问题,严重限制查询优化能力。

SELECT * 会破坏外层过滤和投影下推,视图一旦定义就固化了字段和逻辑,后续查询无法动态干预执行路径——这不是灵活性不足,而是语义锁死。
视图定义里写 SELECT * 就等于放弃所有优化主动权
视图不是函数,不接受参数,也不支持运行时重写结构。一旦定义中用了 SELECT *,哪怕外层只查 id, name,数据库也大概率要先拉全字段、再裁剪,尤其在 PostgreSQL 和 SQL Server 中容易触发物化;MySQL 8.0+ 虽有列裁剪能力,但遇上子查询或 GROUP BY 就失效。
- 外层加
WHERE created_at > '2026-01-01',但视图定义里没这条件 → 优化器无法保证把它下推到基表扫描前,可能先聚合再过滤 - 外层加
LIMIT 10,但视图里含ORDER BY score DESC→ MySQL 可能忽略LIMIT,PostgreSQL 可能全量排序后截断 - 想对视图结果
JOIN其他表,但视图返回了冗余大字段(如TEXT或JSON)→ 内存膨胀、哈希表构建变慢
WHERE 条件无法穿透多层嵌套视图
三层视图嵌套后,外层的 WHERE user_id = 123 很可能只作用于最外层结果集,而不会“穿透”到内层视图所依赖的原始表上。这是因为每层视图都是一次独立的逻辑封装,优化器在展开时会保守处理绑定上下文。
- SQL Server 中,
sp_depends已弃用,sys.dm_exec_describe_first_result_set返回的列信息可能和实际执行计划脱节 - PostgreSQL 的
pg_get_viewdef()能展开定义,但手动拼出的等价 SQL 在加WHERE后,执行计划可能和原视图查询完全不同 - MySQL 5.7+ 默认用
TEMPTABLE算法处理含子查询的视图,意味着外层WHERE根本没机会参与子查询的谓词下推
标量子查询和关联子查询让 ORDER BY、LIMIT 失效
把子查询放在 SELECT 列里(比如 (SELECT COUNT(*) FROM orders WHERE o.user_id = u.id)),语义上就决定了它必须逐行求值。此时外层任何全局控制逻辑(如排序、分页、聚合)都无法影响它的执行时机和范围。
-
ORDER BY发生在子查询计算完成之后,无法利用子查询内部的索引顺序 -
LIMIT 100是对外层最终结果限制,但子查询仍会对全部主表行各执行一次,哪怕你只想要前 100 行的统计 - 想按子查询结果排序?比如
ORDER BY (SELECT MAX(ts) FROM events WHERE events.ref_id = u.id)→ 大概率触发Compute Scalar+ 嵌套循环,且无法用索引加速
视图引用视图时,元数据与执行行为开始脱钩
跨库引用或嵌套引用本身不报错,但权限、架构名、列类型隐式转换等问题会在运行时才暴露。更麻烦的是:同一段视图定义,在不同用户会话下可能生成完全不同的执行计划。
- 用户 A 有
VIEW DEFINITION权限,能看到视图定义;用户 B 没有,调用时直接失败,错误信息却是 “Invalid object name” - 视图 A 引用视图 B,B 中某字段是
VARCHAR(50),A 中 JOIN 时用INT匹配 → 展开后自动加CONVERT,索引失效,但EXPLAIN里未必明显提示 - 修改底层表字段名后,上层视图不会自动失效,
SELECT * FROM upper_view仍能跑,但返回NULL或类型错误值,直到你查具体字段才发现
真正卡住灵活性的,从来不是“能不能写”,而是“写了之后,你还动不了它”。视图越通用,越容易变成黑盒;子查询越靠近 SELECT 列,越难被外部逻辑接管。别指望优化器替你做聪明事——它只在你给足线索时才敢下推、裁剪、合并。











