在 mysql 的 dml 语句中使用 select * 不仅破坏可维护性,更会引发字段错位、类型不匹配、权限越界甚至语法错误——它不是“能跑就行”,而是“一动就崩”。

直接说结论:在 MySQL 的 DML 语句(如 INSERT ... SELECT、REPLACE ... SELECT、UPDATE ... JOIN 子查询等)中使用 SELECT * 不仅破坏可维护性,更会引发字段错位、类型不匹配、权限越界甚至语法错误——它不是“能跑就行”,而是“一动就崩”。
INSERT INTO t1 SELECT * FROM t2 容易字段错位
当目标表 t1 和源表 t2 字段顺序或数量不一致时,SELECT * 会按物理列序硬绑定,而非按名称映射。
- 如果
t2执行过ALTER TABLE t2 ADD COLUMN created_at TIMESTAMP FIRST,SELECT *返回的第一列变成created_at,但t1第一列仍是id→ 插入值错位,id被写入时间戳,整行数据损坏 -
t1(id INT, name VARCHAR(50))和t2(name VARCHAR(50), id INT)表结构相同但顺序不同,INSERT INTO t1 SELECT * FROM t2会把t2.name写进t1.id,触发Incorrect integer value或静默截断 - MySQL 9.6.0 起对 container_aware 场景更敏感,字段错位可能被审计日志捕获为数据一致性事件,但不会回滚
UPDATE ... (SELECT * FROM subquery) 触发列名冲突或解析失败
子查询里用 SELECT *,尤其涉及 JOIN 多表时,MySQL 无法消歧义,直接报错。
-
UPDATE users u SET status = (SELECT * FROM logs l WHERE l.user_id = u.id LIMIT 1)→ 报错Subquery returns more than 1 column,因为logs有id、user_id、action、created_at等多列 - 即使单表,
SELECT *在子查询中也禁止用于标量子查询(必须返回单值),而开发者常误以为“只有一行就安全” - ShardingSphere 或 MyCat 等中间件解析该 SQL 时,因无法推断列名与类型,会拒绝路由或返回空结果,且无明确错误码
权限校验失效:DBA 开的最小权限形同虚设
DBA 给应用账号只授予了 SELECT(id, name, email) 权限,但 SELECT * 会绕过列级权限控制,读出未授权字段。
- MySQL 8.0+ 支持列级权限,但
SELECT *在权限检查阶段被当作“请求所有可见列”,只要用户对表有SELECT权限,就无视列白名单 - 若表含
password_hash、ssn等敏感字段,INSERT INTO audit_log SELECT * FROM users会把它们全 dump 出来,违反 GDPR/等保要求 - 审计日志中该操作标记为
access_type: FULL_TABLE_SCAN,但不会告警“越权读取”,需额外配置列访问监控规则
EXPLAIN 显示 Using temporary + Using filesort 就是 * 在拖后腿
哪怕只是 INSERT INTO tmp SELECT * FROM t WHERE x = ?,优化器也无法预估输出宽度,被迫启用临时表和磁盘排序。
-
SELECT *让优化器放弃估算行大小,转而按最大可能宽度(如 TEXT/BLOB 占位)分配内存 → 触发Using temporary - 如果
t有KEY idx_x (x),但SELECT id,x FROM t WHERE x = ?可走覆盖索引;换成SELECT *后,EXPLAIN中Extra变成Using where; Using temporary; Using filesort - 云环境 buffer pool 命中率低于 75% 时,这种临时表操作会显著拉高
Innodb_buffer_pool_reads和Created_tmp_disk_tables
最常被忽略的是:DML 场景下 SELECT * 的风险比纯查询更高——它不只读,还写、还改、还传播。一次 ALTER TABLE 加字段,可能让运行半年的定时同步任务突然写坏千万条记录,而日志里只有一行 ERROR 1366 或静默丢弃。











