insert()最稳妥:纯新增、不依赖模型、不自动判断逻辑、不隐式丢字段;save()则根据主键存在与否智能切换新增或更新,适合编辑场景。

直接用 insert() 最稳妥,不依赖模型、不自动判断逻辑、不隐式丢字段——除非你明确需要 save() 的“新增 or 更新”智能切换,否则别默认选它。
单条数据该用 insert() 还是 save()?
insert() 是纯新增:传什么字段就插什么(除非字段不存在且没设 strict(false)),成功返回 1,失败抛异常。适合表单提交、API 接口等明确只新增的场景。
-
save()会检查数据里有没有主键(如id):有就走 UPDATE,没有才 INSERT——这在编辑页回传数据时有用,但纯新增时反而容易因误带id导致静默更新 - 如果数据含非表字段(比如前端多传了个
token),insert()默认报错;加strict(false)后直接丢弃,不插也不报 -
insertGetId()是你要的:插入后立刻拿到自增 ID,不用再查一次
批量插入大量数据怎么避免内存爆掉?
insertAll() 一次性塞二维数组很爽,但上万条数据可能 OOM 或超 MySQL max_allowed_packet。必须分批。
- 用
limit(100)控制每批数量:Db::name('log')->limit(100)->insertAll($data) - 别依赖
saveAll()做大批量:它会为每条数据实例化模型,开销大,且失败时整批回滚,不利于定位哪条出错 - MySQL 下想冲突时覆盖(REPLACE INTO),加
replace()链式调用:Db::name('user')->replace()->insertAll($data) - 字段不一致(比如某条缺
email)会导致整批失败,插入前用array_map()统一补默认值或过滤
模型层新增时,create() 和 new + save() 有什么区别?
UserModel::create() 是静态工厂方法,内部做了字段过滤和实例化,比手动 new UserModel() + 赋值更紧凑;但它默认会忽略非表字段,不报错。
立即学习“PHP免费学习笔记(深入)”;
-
create($data, ['name', 'email'], true):第二个参数是白名单字段,第三个是是否 replace,比allowField()更早介入过滤 -
saveAll()返回的是Collection对象,每项带自增id,但不推荐用于超千条数据 - 用
allowField()时注意:它只控制可写字段,不解决字段不存在的问题;仍需配合strict(false)或前置校验 - 模型新增不自动触发事件(如
before_insert)除非你显式开启,这点和 DB 类操作不同
为什么 insert() 报 “Unknown column” 却没看到错误堆栈?
ThinkPHP6 默认开启数据库调试模式时才会显示完整 SQL 和错误,生产环境常被关掉。字段名拼错、大小写不匹配(尤其 MySQL 表名大小写敏感时)、或字段根本没在迁移中创建,都会触发这个异常。
- 先跑
Db::getLastSql()看生成的 SQL,复制到 MySQL 客户端执行,错误更直观 - 确认数据库连接配置里的
DB_CHARSET和表实际字符集一致,否则中文字段名可能解析失败 - 用
Db::name('user')->getFields()动态查当前表字段,比硬编码字段名更安全 - 别信前端传来的字段名——
input('post.', '', null)后务必用白名单过滤,而不是直接丢给insert()
最易被忽略的点:insert() 和 insertAll() 不走模型的验证规则(如 validate() 方法),也不触发事件。要校验,得自己调 Validate 类,或改用模型的 create() / saveAll() 并确保验证已绑定。



















