<p>SELECT * 在生产环境禁用,因其引发IO与网络开销倍增、覆盖索引失效、列结构变更时静默错乱三类隐性风险。</p>

能用,但生产环境里不该用——SELECT * 不是语法错误,而是埋了三类隐性雷:IO 和网络开销翻倍、索引失效、列结构变更时静默错乱。
为什么 SELECT * 会让查询变慢
数据库必须把整行所有字段从磁盘或缓冲区读出来,哪怕你只关心其中一两个。尤其当表里有 TEXT、BLOB 或大 JSON 字段时,IO 压力陡增。更关键的是,它直接废掉覆盖索引:比如在 name 上建了索引,查 SELECT id, name FROM t WHERE name = ? 可以全程走索引不回表;换成 SELECT * FROM t WHERE name = ?,就必须回主键索引捞出所有字段,多一次甚至多次随机 IO。
- MySQL 在 prepare 阶段就展开
*成具体列名,后续加字段不影响已有预编译语句 - PostgreSQL 把
*留到执行期解析,视图里用它等于每次查information_schema.columns,性能略差但语义“活” -
COUNT(*)和SELECT *完全不是一回事:COUNT(*)走元数据或索引统计,基本不读数据页
哪些场景下 SELECT * 真的会出事
最危险的不是慢,是错得悄无声息。比如用 INSERT INTO t2 SELECT * FROM t1,一旦 t1 新增字段或调整顺序,t2 的列对齐立刻错位,可能把邮箱写进用户 ID 字段,且不报错。
- ORM 映射依赖字段顺序,
SELECT *+ 表结构变更 → 字段错位、值静默丢弃 - JOIN 多表时若都有
id,SELECT *返回重复列名,应用层取row["id"]可能拿到任意一个 - 视图定义含
SELECT *,PostgreSQL 下权限控制会失效:基表加列,用户立刻可查到新字段
怎么安全地替代 SELECT *
别靠记忆手敲,也别临时 DESCRIBE table_name 再复制——容易漏、难同步。真正可落地的做法是让工具帮你生成明确列名。
- 开发期用
SELECT column_name FROM information_schema.columns WHERE table_name = 'users' ORDER BY ordinal_position拉出当前列清单,粘贴后删掉不需要的 - IDE 或 CLI 工具(如 DBeaver、MySQL Shell)通常支持右键“Generate SELECT statement”,自动补全列名
- CI 流水线里加 SQL Lint 规则(如 SQLFluff 的
L027),禁止SELECT *出现在.sql文件中 - 真要动态查全部字段?封装成函数或视图时显式写列,而不是把
*当快捷键用
最容易被忽略的点是:问题不在“能不能写”,而在“谁来维护”。一旦表结构动了,SELECT * 查询不会报警,也不会失败,它只是开始返回你没预料到的数据——这种沉默比慢更致命。


















