mysql中是否应该使用'select *'语句?
说到 SQL 查询语句,几乎所有开发者都对 SELECT *
不陌生。在早期学习 SQL 或快速开发原型时,我们可能经常用它来偷懒,因为它看起来既简单又直接:想要啥,全都给。但随着项目的深入和数据库的复杂化,你会发现这个看似万能的工具,其实问题也不少。
今天我们就来聊聊,为什么在许多开发手册中,明确建议避免使用 SELECT *
,以及它为什么仍然在一些场景中大放异彩。
为什么不要用 SELECT *
1. 增加不必要的磁盘 I/O
数据库的核心任务就是读取和存储数据,而数据存储在哪里?没错,磁盘。每次运行查询时,数据库都需要从磁盘读取记录,而 SELECT *
要求数据库返回所有字段——无论你实际需不需要。
特别是对于那些包含大字段的数据类型(比如 TEXT
、BLOB
等),这些字段的读取和传输会显著增加开销。 即便客户端的内存还能装下这些数据,服务端在读取、缓存、传输的过程中,已经付出了高昂的性能代价。
优化建议:明确指定需要的字段。与其要一整车货物,不如点名要几件精致的小物品。
2. 加重网络传输延迟
数据库的查询结果最终会通过网络传输到客户端。如果你在本地环境开发,可能感受不到网络延迟的影响。但在生产环境中,数据库服务器和应用服务往往是分开的,甚至可能跨地域部署。
使用 SELECT *
,你可能会无意识地传输大量冗余数据,尤其当你的表包含很多列或大字段时。更何况,如果网络状况不稳定,这些多余的数据会进一步放大延迟。
举个例子,如果你只需要 id
和 name
两列,却用 SELECT *
把几十列全拖了过来,网络的压力显而易见。
3. 阻碍索引优化
索引是数据库优化的灵魂,但 SELECT *
却常常毁了它。看个简单的例子:
CREATE TABLE `user_innodb` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`gender` tinyint(1) DEFAULT NULL,
`phone` varchar(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_NAME_PHONE` (`name`, `phone`) USING BTREE
) ENGINE=InnoDB;
假设我们运行以下查询:
SELECT * FROM user_innodb WHERE name = 'Alice';
MySQL 可以用 IDX_NAME_PHONE
索引快速定位 name='Alice'
的记录,但由于 SELECT *
请求了所有字段,MySQL 不得不回表(通过主键检索完整记录)。这个回表操作直接增加了查询的时间复杂度。
而如果我们明确指定查询的字段:
SELECT name, phone FROM user_innodb WHERE name = 'Alice';
索引本身就可以满足查询,完全不需要回表,大大提升了效率。
4. 拖慢 JOIN 查询
多表连接查询中,SELECT *
的坑更大。假如我们有两张表:
SELECT * FROM t1 JOIN t2 ON t1.id = t2.id;
每次连接,MySQL 都需要从两个表中各自提取所有字段。如果其中某张表字段过多,不仅会浪费内存,还会让查询过程变得拖沓。尤其当连接缓冲区(join buffer)被大量无用数据占用时,性能损失会更加明显。
更可怕的是,开发人员往往在生产环境中才意识到这个问题。到那时,想修复代价可能已经很高了。
为什么还用 SELECT *
?
尽管 SELECT *
有以上种种问题,但它也并非一无是处。在某些特定场景下,它的灵活性和便捷性难以替代。
1. 简化开发过程
当你快速搭建一个项目原型,或是在初期摸索数据库表结构时,SELECT *
的便利性无可比拟。你无需记住表中有哪些字段,也不需要因为字段的频繁增删而不断调整查询语句。
比如,你在构建一个用户表时,刚开始可能只包含 id
和 name
。后来需求变了,又加了 email
、address
等字段。此时用 SELECT *
可以让开发更流畅,避免频繁修改 SQL。
2. 减少维护成本
在一些大型项目中,表结构变动很常见。如果 SQL 语句中显式写了所有字段名,那每次改表都可能需要逐一修改相关的查询代码。而 SELECT *
则避免了这些麻烦。
比如,一个电商系统中的订单表可能会不断扩展字段:最初只有 id
、total_price
,后来加了 shipping_address
、payment_status
,再后来又加了 discount_code
等。每次字段变动都去改 SQL,简直是灾难。
使用 SELECT *
,你可以让 SQL 语句保持稳定,哪怕表结构改了,查询依然不会报错。
3. 方便调试和测试
调试 SQL 时,最重要的是迅速看到数据结果。如果你要对某张表的多个字段进行验证,而这些字段名又可能不熟悉,用 SELECT *
就能一次性拉出所有数据,方便你快速找到问题。
这在复杂的多表查询中尤为有用。比如,你可能需要检查某个字段是否为空、某些数据是否匹配预期。如果每次都手动指定字段,不仅麻烦,还容易漏掉关键数据。
4. 灵活应对动态需求
某些应用场景下,查询需求往往是不确定的。比如,一个数据查询工具,允许用户自定义选择查询哪些字段。此时,用 SELECT *
可以避免遗漏字段,保证数据完整性。
此外,对于临时性查询任务,比如生成报表或导出全量数据,SELECT *
的灵活性非常适合快速完成工作,而无需花时间去明确字段。
总结:用还是不用?
SELECT *
的确简单方便,但它的问题同样显而易见。在生产环境中的查询而言,性能往往是第一要务,所以尽量避免使用 SELECT *
,显式列出所需字段才是最佳实践。
但在开发、调试阶段,或者一些动态查询的场景中,SELECT *
的便利性又不可忽视。
所以,这就像是一把双刃剑——关键在于你是否清楚自己使用它的目的,并明白后果。个人推荐的话, 若表的宽度小于10且表中不含TEXT
、BLOB
等较大的字段时 ,则使用SELECT *
,若表宽度大于10或含TEXT
、BLOB
等较大的字段时,则使用列名。