SELECT * 会显著降低查询性能并引发多种风险:增加I/O与网络开销、阻碍覆盖索引使用、导致回表、影响执行计划稳定性、妨碍分区裁剪、在表结构变更时易出错,仅适用于临时排障、调试或极小表。

SELECT * 会拖慢查询速度
数据库要读取整行所有字段,哪怕你只用其中一两个。磁盘 I/O 和网络传输量都变大,尤其表里有 TEXT、BLOB 或长字符串字段时,性能下降更明显。如果加了索引覆盖优化,SELECT * 还会让优化器放弃使用覆盖索引,转而回表查数据。
- 索引只包含部分列,但
SELECT *要求所有列,MySQL/PostgreSQL 只能先走索引找主键,再回聚簇索引捞全行 - 宽表(比如 50 列)+ 高并发查询,内存和网卡容易成瓶颈
- ORM 自动生成
SELECT *(如 Django 的Model.objects.all())时,要特别留意实际需要哪些字段
SELECT * 在表结构变更时容易出错
字段顺序或类型变化后,应用层按位置取值的代码(比如 JDBC 的 ResultSet.getObject(2))会直接错位;用 ORM 映射时也可能因新增 NOT NULL 字段没设默认值而抛异常。
- 添加新列:旧版应用若依赖列序,
SELECT *返回多一列,下标越界或类型不匹配 - 删除/重命名列:查询仍成功,但应用拿到
null或空字符串,逻辑出错且难定位 - 修改字段类型(如
VARCHAR(255)→TEXT):某些驱动可能截断或报转换错误
SELECT * 影响查询计划稳定性
优化器基于统计信息估算成本,而 * 让它无法预判实际需要哪些列,导致执行计划波动。例如 PostgreSQL 的 pg_stat_statements 会把不同字段列表的语句视为不同查询,但 SELECT * 看似统一,实则每次解析都要重新推导列集合,缓存命中率低。
- 同一张表,
SELECT a,b FROM t和SELECT a,c FROM t可能复用同一个索引;但SELECT *强制走全表扫描或更大范围索引 - 分区表中,
SELECT *可能阻止分区裁剪(Partition Pruning),扫到本不该访问的分区 - 物化视图或查询重写规则(如 MySQL 的 Query Rewrite Plugin)对
*支持有限,容易绕过优化
什么时候可以勉强用 SELECT *
仅限于临时排障、开发调试、或明确知道表极小(SELECT * —— 因为视图一旦创建,底层表改了,视图不会自动更新字段列表。
- MySQL 视图创建时会固化列定义,但用
SELECT *创建的视图,后续ALTER TABLE加字段不会反映在视图里 - 用
SELECT *写迁移脚本?小心目标库字段顺序不同,INSERT ... SELECT *插入错列 - DBA 做巡检时敲
SELECT * FROM pg_stat_activity LIMIT 5没问题,但写进监控脚本就得固定字段
真正麻烦的不是语法本身,而是它掩盖了数据访问意图——你到底要什么字段、为什么需要、谁在用、会不会变。漏掉一个 created_at 可能让业务日志少关键时间戳,多查一个 user_avatar 可能让接口慢 200ms。










