为什么在SQL视图定义中不建议使用SELECT 星号 而应明确指定列名

作者:袖梨 2026-06-23
SQL视图中禁用SELECT *,因其导致列结构不可控、跨平台不一致、列名冲突、解析失败、性能下降及工具链失效。

在 SQL 视图定义中写 SELECT * 不是语法错误,但等于主动放弃对输出结构的控制权——视图一旦创建,列名、列序、列数就该是确定的,而 * 让它变成运行时才决定的黑盒。

视图列结构会被底层表变更悄悄破坏

视图定义本身不存储字段元数据,只存 SQL 文本。当你用 SELECT * 创建视图后:

  • 后续对源表执行 ALTER TABLE ADD COLUMN updated_at DATETIME,视图查询结果会立刻多出一列,但视图定义里根本没这行代码
  • 如果下游应用按位置取值(如 rs.getString(2)),或者 BI 工具靠列名自动映射字段,就会拿到错的数据甚至直接报错
  • SQL Server 和 Access 对 * 的解析逻辑不同,跨平台迁移时容易出现列序不一致

JOIN 多表时 SELECT * 必然触发列名冲突

两个表都有 id 字段?name 字段?created_at 字段?SELECT * 会让数据库当场报错:Column 'id' is ambiguous

你必须显式限定:

  • 用表别名前缀,比如 users.id AS user_idorders.id AS order_id
  • 含空格或特殊字符的字段名(如 [E-mail Address])必须加方括号,否则解析失败
  • 同语义字段需统一别名,例如 users.full_name AS nameadmins.display_name AS name,避免下游硬编码依赖原始名

性能与执行计划会被 * 拖垮

SELECT * 不仅读得多,还让优化器“猜不透”你真正要什么:

  • 本可走覆盖索引的查询(如 WHERE status = 1 上有索引),改成 SELECT * 后,优化器发现索引里没有全部字段,被迫回表——EXPLAINExtra 列从 Using index 变成 Using where; Using index; Using temporary
  • 字段含 TEXTBLOB 时,单行物理大小暴涨,SELECT * 会把整页数据全读进 buffer pool,哪怕你只关心 idtitle
  • MySQL 的 max_allowed_packet 默认 4MB,大字段 + * 容易触发 Packets larger than max_allowed_packet are not allowed

工具链和 ORM 映射会因 * 失效

MyBatis 的 resultMap、JPA 的 @Column、Power BI 的自动列推断,全都依赖稳定且可预测的列名与顺序。

一旦源表新增字段,SELECT * 视图返回的列序可能变化(尤其在 MySQL 中 ALTER TABLE … FIRST / AFTER 改过字段位置),导致:

  • MyBatis 把新字段值错映射到老字段属性上
  • Power BI 刷新报表时提示 “column not found” 或列类型错乱
  • Java 应用用 ResultSet.getObject(i) 按索引取值,拿到完全无关的数据

最麻烦的是:这类问题往往不报错,只静默错位——查数据时看着都对,上线跑几天才发现统计口径全歪了。

相关文章

精彩推荐