db::table()->insert() 是最快批量插入方式,要求数据为索引数组且子数组为关联数组、字段顺序一致、无空值;超千行或含特殊类型时应改用 db::statement() + pdo quote 安全拼接原生 sql。

用 DB::table()->insert() 批量插入最直接
只要数据结构规整(每条记录字段顺序一致、不含空值或特殊类型),DB::table('users')->insert($data) 是最快的方式。Laravel 会自动把二维数组转成单条 INSERT INTO ... VALUES (),(),() 语句,避免 N 次查询。
注意:$data 必须是索引数组,且每个子数组必须是关联数组(键名对应字段名),不能混用数字键;否则 Laravel 会抛出 Array to string conversion 错误。
- ✅ 正确写法:
[ ['name' => 'A', 'email' => 'a@b.c'], ['name' => 'B', 'email' => 'b@b.c'] ] - ❌ 错误写法:
[ [0 => 'A', 1 => 'a@b.c'], [...] ]或[ ['name' => null, 'email' => 'x'], [...] ](null值需显式允许字段为 nullable) - ⚠️ 单次插入不宜超过 1000 行,否则可能触发 MySQL 的
max_allowed_packet限制或内存溢出
大数量插入要用 DB::statement() + 原生 SQL
当要插几万行,或者字段含 JSON、二进制、时间戳等易被 Eloquent 自动转换的类型时,insert() 会变慢甚至失败。这时应绕过 Query Builder,手拼 SQL 后用 DB::statement() 执行。
关键点不是“怎么拼”,而是“怎么安全拼”:必须用 DB::connection()->getPdo()->quote() 对每个值做转义,不能直接字符串拼接,否则有 SQL 注入风险。
- 不要用
str_replace或sprintf手动加引号 - 批量生成 values 部分时,每行都调用
$pdo->quote($val),哪怕$val是整数(PDO 会原样返回) - MySQL 5.7+ 支持
INSERT ... ON DUPLICATE KEY UPDATE,如需去重插入,可在此阶段一并写入
带自增 ID 或时间戳时,别依赖 insert() 返回值
DB::table()->insert() 总是返回 true,不返回插入后的 ID 列表。如果你需要知道每条记录的 id(比如后续要关联写入中间表),就不能用它。
- 方案一:改用
DB::table()->insertGetId()—— 但只适用于单条插入 - 方案二:先批量插入,再用
DB::select('SELECT LAST_INSERT_ID(), ROW_COUNT()')获取起始 ID 和总行数,自行推算 ID 范围(仅限 InnoDB + auto_increment) - 方案三:用事务包裹 N 次单条
insertGetId(),但性能下降明显,仅适合 ≤ 100 行且必须回传 ID 的场景
用 upsert() 替代“查再插”逻辑(Laravel 9+)
如果业务本质是“存在则忽略,不存在则插入”,别自己写 where()->first() + save() 循环。Laravel 9 起支持 upsert(),底层映射到数据库的 INSERT ... ON CONFLICT(PostgreSQL)或 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)。
用法:DB::table('users')->upsert($data, ['email'], ['name', 'updated_at']),其中第二个参数是唯一索引字段(如 email),第三个是冲突时更新的字段。
- ⚠️ 前提是数据库表上已建好对应唯一索引,否则
upsert()会报错或行为异常 - MySQL 下,若唯一索引是联合索引(如
UNIQUE KEY `uid_type` (`user_id`, `type`)),$uniqueBy必须传['user_id', 'type'],顺序和索引定义严格一致 - SQLite 不支持此方法,调用会抛出
NotSupportedException
innodb_flush_log_at_trx_commit(仅开发环境),或把多条 insert 包在事务里——这点容易被忽略。











