不能直接用insert into table1 select * from table2,因为字段顺序/数量/类型不匹配会导致错位、截断或报错;大表会锁表、触发max_allowed_packet限制、日志暴涨及mdl锁阻塞;必须显式指定字段、分批处理并校验兼容性。

直接用 INSERT INTO ... SELECT 会锁表、OOM 或超时,尤其当单表超千万行时。必须分批插入 + 显式字段对齐 + 主键冲突处理。
为什么不能直接用 INSERT INTO table1 SELECT * FROM table2
常见错误现象:Column count doesn't match value count 或插入后字段错位、数值截断;大表执行中可能触发 max_allowed_packet 限制或事务日志暴涨;InnoDB 表在非只读模式下会长时间持有 MDL 锁,阻塞其他 DML。
- 源表和目标表字段顺序不一致时,
SELECT *会按物理列序取值,极易错配 - 目标表若有自增主键且未显式指定
id,MySQL 可能拒绝插入(取决于sql_mode) - 没加
WHERE条件的全表扫描,在无索引字段上会变全表锁定
必须显式写出字段名并校验类型兼容性
字段名顺序、数量、类型三者必须严格一致。比如 INT 和 BIGINT 虽可隐式转换,但 VARCHAR(50) 插入 VARCHAR(20) 字段会截断,且默认不报错(取决于 STRICT_TRANS_TABLES 是否启用)。
- 写法必须是:
INSERT INTO target_table (id, name, created_at) SELECT id, name, created_at FROM source_table - 所有
SELECT子句字段顺序要完全相同,不能靠别名“蒙混过关” - 跨库合并时,用
db_name.table_name.column全限定名避免歧义
大表合并必须分批次 + 控制事务大小
一次性插入百万级数据极易触发回滚段溢出或复制延迟。推荐每次 1–5 万行,用主键或时间字段分片。
- 示例(按主键分页):
INSERT INTO user_all SELECT id, name, city FROM user_2024 WHERE id BETWEEN 100001 AND 110000 - 每批执行后加
COMMIT,避免长事务拖慢 binlog 写入 - 若目标表有唯一索引,用
INSERT IGNORE或ON DUPLICATE KEY UPDATE处理重复 - 执行前先关闭自动提交:
SET autocommit = 0,手动控制 commit 频率
异库或跨实例合并慎用 Navicat “复制表” 功能
Navicat 的图形化复制看似方便,但默认会重建表结构、清空目标表,且“遇到错误时继续”选项实际可能跳过关键约束校验,导致数据静默丢失。
- 真正安全的做法:导出 SQL(仅数据)、修改
INSERT语句为带字段名的批量插入 - 禁用 “创建表” 和 “删除对象”,否则可能误删生产表
- 传输前务必比对两库的
CREATE TABLE输出,确认CHARSET、COLLATION、ENGINE完全一致
最容易被忽略的是字段隐式转换和事务隔离级别影响——比如 READ-COMMITTED 下分批插入时,中间新写入的数据可能被后续批次重复读取。合并前最好先 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 并全程保持该级别。











