INSERT 是什么
INSERT 向表中写入新行,支持单行、多行批量以及 INSERT ... SELECT 从其他表或查询结果导入。批量插入把多行放进一条语句,能同时减少网络往返、SQL 解析次数与日志写入开销,是导数场景的首选写法。写入时务必显式列出列名:不写列清单的语句依赖表的列顺序,一次加列或调序就可能把数据写进错误的列。
基本信息
| 用途 | 向表中新增行 |
|---|---|
| 语法 | INSERT INTO 表 (列1, 列2) VALUES (值1, 值2)[, (值)...] |
| 批量导入 | INSERT INTO ... SELECT ... |
| 冲突处理 | 默认报错;忽略冲突与冲突即更新属 MySQL 扩展写法 |
| 自增列 | 通常省略不写,由系统分配值 |
| 事务建议 | 批量导入放进一个事务并分批提交 |
典型使用场景
- 单条业务数据录入,显式写列名,便于后续加列时语句仍可用
- 初始化字典与配置表,一条语句插入多行
- 把历史表数据搬到归档表,用导入查询完成搬移
- 根据分组统计结果构建日汇总表,一次写入全部分组
- 大批量数据导入时使用批量语句,提升整体吞吐
- 造数压测:批量生成测试数据,同时留意唯一约束冲突
常用写法
常用写法
INSERT INTO users (name, email) VALUES ('a', 'a@example.com'); # 单行插入
INSERT INTO orders (user_id, sku, qty) VALUES (1,'SKU-1',2),(1,'SKU-2',1),(2,'SKU-9',5); # 多行批量
INSERT INTO orders_archive SELECT * FROM orders WHERE created_at < '2025-01-01'; # 表间搬移
INSERT INTO stats_daily (d, cnt) # 由查询结果直接构造目标行
SELECT DATE(created_at), COUNT(*) FROM orders GROUP BY DATE(created_at);批量导入模板
批量导入模板
SET autocommit = 0; # 关掉逐行提交,减少日志刷盘次数
INSERT INTO orders (user_id, sku, qty) VALUES
(1,'SKU-1',2),(1,'SKU-2',1),(2,'SKU-9',5); # 单条语句控制在数百到一千行
COMMIT; # 一次提交,缩短事务持有时间冲突处理写法对照
| 写法 | 行为 | 注意点 |
|---|---|---|
| INSERT | 主键或唯一键冲突时报错,整条语句失败 | 语义最清晰,无额外开销 |
| INSERT IGNORE | 冲突行被跳过,只留警告 | 可能掩盖真实错误,如数据被截断 |
| 冲突即更新 | 已存在则改为更新 | 自增值会被消耗,批量导入出现跳号 |
| REPLACE INTO | 先删除同键旧行再插入 | 删行会触发级联删除,等于换了一行,慎用 |
| INSERT ... SELECT | 从查询结果导入 | 大表建议分批,避免单个大事务 |
| 自增列省略 | 由系统分配 | 显式写 0 也可以,但省略最清晰 |
批量插入建议单条语句控制在数百到一千行并显式指定列:条数太少网络往返多,条数太多会让 binlog 事件与回滚段膨胀,反而拖慢导入。
需要导入千万级数据时优先考虑服务端装载工具或文件直接加载,逐条插入的网络往返与 SQL 解析开销会明显拖慢整个过程。
INSERT ... SELECT 在源表与目标表相同时属于边读边写,结果可能不确定;搬迁这类数据应先取数到临时表,再从临时表插入目标表。
写入检查清单
- 显式列出全部列名,值的顺序与列的顺序严格一一对应
- 批量语句单次控制在数百到一千行,避免单个超大事务
- 导入前关闭自动提交,导入结束统一提交一次
- 自增列不要显式赋值,避免后续插入冲突与索引跳号
- 导入前确认目标表的唯一键与索引,冲突处理策略事先定好
- 导入完成后核对行数与校验值,别只看语句是否执行成功
写入完成后不要只看语句是否成功:返回的影响行数会受冲突处理方式影响,数字对不上通常意味着有行被跳过或被更新,正式导入后应单独统计计数做校验。