视图定义中禁用select *,因其使列名与列序无法固化为契约,基表结构变更会导致下游代码静默错位或崩溃;必须显式指定列名并加as别名,才能保障字段可控、权限可配、跨库一致。

为什么视图定义里用 SELECT * 会立刻破坏下游代码
因为视图列名和列顺序在创建时就固化为契约,而 SELECT * 让数据库“猜”字段——一旦基表新增、删减或重排字段,视图输出结构就无声变更。下游应用若依赖 ResultSet.getObject(2) 或 MyBatis 的自动映射,轻则取错值(比如把 updated_at 当成 created_at),重则直接抛 Column index out of range。这不是概率问题,是必然发生的时间差故障。
SELECT * 在视图中无法真正“隐藏”敏感字段
有人误以为建个视图再加 WHERE 1=0 或 CASE WHEN FALSE THEN password_hash END 就能掩码,其实完全无效:
-
WHERE 1=0→ 视图返回空结果集,不是隐藏字段 -
CASE表达式仍保留password_hash列,元数据可见、类型暴露、ORM 仍会分配内存 - 真正安全的隐藏,只有一种:在视图
SELECT子句里根本**不写**该字段,例如SELECT id, email, status FROM users - 新增敏感列(如
internal_notes)后,视图不会自动排除,必须人工更新定义
不同数据库对 SELECT * 视图的解析行为不一致,排查极难
MySQL 可能静默接受两个同名列(如 SELECT u.id, o.id FROM users u JOIN orders o),但实际只保留后一个 id;PostgreSQL 和 SQL Server 则直接报错。这种差异导致开发环境看似正常,上线后在别的数据库上崩掉,错误日志里只有模糊的 Unknown column 或空结果,源头很难定位到视图定义本身。
视图列名契约失效后,连 ORDER BY 和 GROUP BY 都会出错
视图列名一旦由 SELECT * 推导而来,就失去可控性:
-
ORDER BY必须用视图列名,但你根本不知道它叫id还是users_id -
GROUP BY引用别名会失败(SQL 执行顺序决定WHERE/GROUP BY看不到SELECT中的AS别名) - ClickHouse 等引擎要求
GROUP BY必须字面匹配视图列名,连大小写都敏感 - 后续想加
GRANT SELECT (email) ON safe_users TO app_user细粒度权限?列名不确定就做不到
最麻烦的不是建视图那一刻,而是六个月后别人改了基表,你查不出谁动了哪条 SELECT * 视图——它没报错,只是悄悄让所有调用它的服务开始返回错数据。










