insertall报duplicate entry错误是因为底层执行标准insert语句,不带ignore或on duplicate key update,任一主键/唯一索引冲突即整批失败;单次建议200–500条,超量需分片;返回影响行数而非布尔值,事务中必须显式try/catch回滚。

insertAll 是 ThinkPHP6 最常用的批量插入方法,但它不是“开箱即用就高效”的银弹——用错参数、忽略约束、不控数量,反而比单条 insert 更慢、更容易崩。
insertAll 为什么会报 Duplicate entry 错误
insertAll底层走的是标准INSERT INTO table (...) VALUES (),(),(),不带IGNORE或ON DUPLICATE KEY UPDATE只要其中任意一行触发主键或唯一索引冲突,整个 SQL 就会失败,抛出类似:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'xxx' for key 'PRIMARY'它不会跳过冲突行,也不会返回已成功插入的条数,而是直接中断
-
如果你预期数据可能重复,又不想报错,有三个选择:
- 改用
replace()->insertAll($data)(注意:它先DELETE再INSERT,会重置自增 ID、触发外键级联删除) - 手动拼
INSERT IGNORE INTO,用Db::execute()执行原生 SQL - 先
SELECT查重,过滤掉已存在的记录再插入(适合小批量、有明确去重字段的场景)
- 改用
insertAll 单次传多少数据最稳
- TP6 默认不限制数组长度,但 MySQL 有
max_allowed_packet和 PDO 参数绑定上限 - 实测中,超过 500 条容易触发:
PDOException: SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders - 生产环境建议:
- 单次
insertAll控制在 200~500 条之间 - 超过 1 万条时,务必分片处理,例如:
$chunks = array_chunk($data, 300); foreach ($chunks as $chunk) { Db::name('user')->insertAll($chunk); } - 不要用
limit(300)->insertAll($data),limit在insertAll中无效(它是查询构造器方法,对写操作无作用)
- 单次
insertAll 返回值和异常处理必须手动兜底
成功时返回插入行数(int),比如
3;失败时抛异常但不终止脚本,除非你 catch-
常见疏漏:
- 在事务里混用
insertAll和updateAll,却没try/catch,导致部分写入成功、部分失败,事务没回滚 - 把返回值当布尔判断:
if (Db::name('x')->insertAll($data)) { ... }—— 这样写是错的,0 行也可能是合法结果(比如空数组),而异常根本不会进 if 分支
- 在事务里混用
-
正确做法示例:
try { Db::startTrans(); $affected = Db::name('log')->insertAll($logs); Db::commit(); } catch (\think\db\exception\DataNotFoundException $e) { Db::rollback(); // 记录 $e->getMessage() 用于排查 }
真正影响批量插入效率的,从来不是方法名本身,而是你有没有提前检查主键/唯一索引冲突、有没有控制单批大小、有没有在事务里做异常兜底。这些细节不处理,insertAll 再快也会卡在第一批次。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











