在SQL查询编写过程中,SELECT *是很多开发者高频使用的写法,它不需要手动列出所有字段就能获取表中的全部数据,看似便捷的背后其实隐藏着不少真实代价,这些代价会在不同场景下影响系统的整体表现。

增加不必要的数据传输与内存消耗
当使用SELECT *时,数据库会把表中所有字段的数据都返回给客户端,哪怕你实际只需要其中1到2个字段。如果表中包含大字段比如TEXT、BLOB类型,或者字段数量很多,多余的数据会占用额外的网络带宽,同时客户端接收数据后也需要分配更多内存来存储这些无用内容。
比如有一张用户表包含id、name、age、avatar_url、personal_intro五个字段,你只需要查询用户的id和name,使用SELECT *的查询和指定字段的查询对比如下:
-- 不必要的全字段查询 SELECT * FROM user WHERE age > 18; -- 合理的指定字段查询 SELECT id, name FROM user WHERE age > 18;
影响索引的有效使用
数据库查询时如果查询的字段都包含在某个索引中,就可以直接通过索引返回数据,不需要回表查询,这种索引也叫覆盖索引。如果使用SELECT *,需要返回所有字段,几乎不可能所有字段都被同一个索引包含,因此大概率会触发回表操作,增加查询的IO消耗。
假设我们给user表的age字段建立了普通索引,执行查询时两种写法的执行过程差异如下:
| 查询写法 | 执行过程 |
|---|---|
| SELECT age FROM user WHERE age > 18 | 直接从age索引中获取数据,不需要访问表数据,查询速度快 |
| SELECT * FROM user WHERE age > 18 | 先从age索引找到符合条件的id,再回表查询所有字段数据,增加IO操作 |
引发表结构变更后的兼容性问题
如果后续表结构发生变更,比如新增了字段、删除了字段或者修改了字段顺序,使用SELECT *的查询返回的结果集结构会直接发生变化,可能导致上层业务代码出现解析错误。而指定字段的查询只会返回你明确声明的字段,只要这些字段没有被修改,业务代码就不需要调整。
比如原本user表只有id和name两个字段,业务代码按SELECT *返回的顺序取第一个值为id,第二个值为name。后续给表新增了create_time字段放在最前面,SELECT *返回的第一个值就变成了create_time,业务代码就会取到错误的数据。
增加查询优化器的解析成本
数据库查询优化器在处理SELECT *时,需要先解析表结构,获取所有字段的列表,再生成执行计划,这比直接处理指定字段的查询多了一步解析操作。虽然单条查询的解析成本很低,但在高并发场景下,大量SELECT *查询的额外解析成本累积起来也会对数据库性能产生可感知的影响。
更合理的查询写法建议
日常编写SQL查询时,建议遵循以下原则减少SELECT *的使用:
- 明确列出需要查询的字段,只获取业务真正需要的数据
- 如果确实需要查询所有字段,确认表结构稳定且字段数量少、没有大字段后再使用
- 联表查询时给每个字段加上表别名,避免字段名冲突,也更清晰
以下是联表查询的正确示例:
-- 联表查询指定字段并加别名 SELECT u.id AS user_id, u.name AS user_name, o.order_no FROM user u JOIN order o ON u.id = o.user_id WHERE u.age > 18;
注意:在某些ORM框架中,虽然代码层面没有写SELECT *,但框架生成的SQL可能默认查询所有字段,需要手动配置需要查询的字段属性来避免这个问题。
总的来说,SELECT *的真实代价体现在性能、稳定性、可维护性多个维度,养成指定查询字段的习惯,能让SQL查询更高效,也能减少后续系统维护的风险。