前天帮同事把一张 20 万行的订单表迁到新库,SQL 写好一跑,报错 1136:Column count does not match value count at row 1。他用的就是 insert into select用法,字段顺序没对上。这个语法本身不复杂,坑都在字段和事务的细节里。

基本写法:目标字段和查询字段一一对应

完整结构是 insert into 目标表(字段1,字段2) select 字段1,字段2 from 来源表 where 条件。目标字段和查询字段数量、顺序、类型都要对上,字段名建议写全,别图省事省略。目标表的自增主键可以不写,让数据库自己生成,写了反而容易和来源表撞号。

insert into order_bak (id, user_id, amount, created_at) select id, user_id, amount, created_at from orders where created_at >= '2026-01-01';

整表复制和批量造测试数据

要复制整张表,先 create table order_bak like orders 建结构,再 insert into order_bak select * from orders,两表结构完全一致时最省事。只复制部分字段也行,但没写的字段会取默认值,not null 又没有默认值的列会直接报 1364。造测试数据时,用 select 拼常量再配合 union all,一次插入几千行,比循环执行单条 insert 快很多。数据量再大就分批,每 5000 行提交一次,别让一个长事务把 undo 日志撑爆。

主键冲突、唯一索引和锁

目标表已有相同主键时,普通 insert 报 1062 Duplicate entry。MySQL 可以用 insert ignore 跳过冲突行,或者 on duplicate key update 改成更新,但这两种写法会把其他错误一起吞掉,生产环境用之前想清楚。来源表很大时,select 部分会加锁,凌晨跑比较稳妥。执行前先 select count(*) 看一眼行数,跟目标表核对,再决定要不要加 where 限制范围。

一句话:字段列表别省,类型和顺序核对一遍,大表分批提交,insert into select用法就没什么玄学。真出错了先看错误码,1136 查字段数,1062 查主键,1364 查非空列。