SELECT * 不能查出“全部字段”,它只返回FROM子句指定对象中元数据定义的可见字段,不包括计算列、权限隐藏列、失效临时列及JOIN后未去重的同名列。

SELECT * 真的能查出“全部字段”吗?
不能。SELECT * 只返回当前 FROM 子句指定表(或视图、子查询)中**定义的可见字段**,不包括:计算列(除非显式定义为生成列)、注释字段、被 REVOKE 权限隐藏的列、临时表中已失效的列,以及 JOIN 后未去重的同名列(会报错或被自动别名化)。实际执行时,MySQL 会先解析表结构,再展开为具体字段列表——所以它查的是“元数据里此刻存在的字段”,不是“你印象中的全部”。
想安全获取某张表所有字段名,用 INFORMATION_SCHEMA
依赖 SELECT * 写法做自动化或建模是危险的,字段增减会导致结果错位或程序崩溃。更可靠的方式是查系统表:
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'your_db_name' AND TABLE_NAME = 'your_table_name' ORDER BY ORDINAL_POSITION;
注意:TABLE_SCHEMA 必须写对,尤其在多库共用连接时;ORDINAL_POSITION 是真实定义顺序,比按 COLUMN_NAME 排序更准确。
SELECT * 在 JOIN 场景下容易触发错误或歧义
当多个表有同名字段(比如都含 id、created_at),直接 SELECT * 会导致结果集字段名冲突,PHP/Python 的 fetch 操作可能只取到其中一个值,甚至 MySQL 8.0+ 会直接报 ERROR 1052 (23000): Column 'xxx' in field list is ambiguous。
- 必须显式列出字段,并用表别名限定:
SELECT u.id, u.name, o.status FROM users AS u JOIN orders AS o ON u.id = o.user_id - 如果真需要全部字段且确定无冲突,可用
tbl.*限定范围:SELECT u.*, o.order_no, o.amount - 避免
SELECT * FROM a JOIN b这种裸写法——即使当前没同名字段,后续加字段就埋雷
性能和可维护性上,* 比显式列名慢且难调试
MySQL 优化器对 SELECT * 的处理并不“智能”:它仍需读取所有字段对应的数据页,即使你只用其中 2 列;网络传输、序列化、内存分配开销都更高。更重要的是,日志、监控、慢查分析里看到的都是 *,无法快速定位是哪个字段拖慢了查询。
- 线上环境禁止在大表(行数 > 10 万 或 单行宽度 > 2KB)上用
SELECT * - ORM 中配置
select: ['id', 'title']明确优于select: '*' - 开发阶段用
SELECT *快速验证可以,但提交前必须换成明确字段列表
字段顺序、是否允许 NULL、默认值这些细节,只有显式写出字段才能让 SQL 自带文档性。靠 * 隐藏复杂度,最后只会让问题更难定位。










