更新与删除:update、upsert 与原子操作
本教程共 54 篇 · 第 20 篇 · 更新于 2026-08-11 · 约 4 分钟阅读
本节目标:掌握单条与批量的更新删除、幂等的 upsert、数值原子操作,并理解级联删除的陷阱。
改和删比查更需谨慎。这一章把更新、删除、幂等三件事讲全:单条与批量怎么改、怎么删,upsert 怎么做到「有就更新、没有就建」,以及级联删除有哪些坑。
更新单条:update
const user = await prisma.user.update({
where: { id: 1 },
data: { name: "新名字" },
});
where 必须唯一字段,data 里只写要改的字段,其余原样保留。记录不存在抛 P2025。update 也能带关系字段做嵌套更新,比如顺带改用户的资料表,这个能力在第 24 章展开。
批量更新:updateMany
const result = await prisma.post.updateMany({
where: { published: false },
data: { published: true },
});
// { count: 19 }
where 可以随意过滤,返回的是受影响条数 { count },不是记录本身。想拿回更新后的记录列表,用 updateManyAndReturn(PostgreSQL、CockroachDB、SQLite 支持):
const users = await prisma.user.updateManyAndReturn({
where: { role: "USER" },
data: { isActive: true },
});
upsert:更新或创建,一步到位
const user = await prisma.user.upsert({
where: { email: "viola@prisma.io" },
update: { name: "Viola" },
create: { email: "viola@prisma.io", name: "Viola" },
});
先按唯一字段找,找到走 update,找不到走 create。两次执行结果一致,天然幂等,适合初始化数据、每日签到、统计计数这类逻辑。update 传空对象即可模拟 findOrCreate——「有就不动,没有就建」。
数值原子操作
计数加一、减库存这类场景,不能先读后写。两个请求同时读到 5,各自加一写回,结果变成 6 而不是 7。要用原子操作符,让数据库在内部完成计算:
await prisma.post.update({
where: { id: 1 },
data: {
views: { increment: 1 },
likes: { increment: 1 },
},
});
四个操作符:increment、decrement、multiply、divide。点赞数、库存、余额这些高频变动的数字都该这么改,并发请求不会互相覆盖。
删除:delete / deleteMany
// 删单条,where 必须唯一字段
await prisma.post.delete({ where: { id: 1 } });
// 按条件批量删,返回 { count }
const result = await prisma.session.deleteMany({
where: { expiresAt: { lt: new Date() } },
});
// 清空整表,慎用
await prisma.user.deleteMany({});
delete 返回被删记录,deleteMany 返回 { count }。deleteMany({}) 删光所有行,调试环境常用。删除前记得校验归属:where 里带上 userId 之类的归属字段,防止越权删除别人的数据。
级联陷阱
必填关系下,删除带子记录的用户会失败,报错大意是「这个操作会违反 PostToUser 的必填关系」。三种解法:
- 把关系改成可选(Post.author 加
?),配合引用操作onDelete: SetNull - 先删子记录再删父记录,两条命令包在 $transaction 里,全部成功才提交
- 在 Schema 里声明
onDelete: Cascade,让数据库自动级联删除
Note级联删除是 Schema 层面的配置,不是查询参数。引用操作的默认值(必填关系 Restrict、可选关系 SetNull)在第 14 章讲过,此处不再重复。
测试环境清空数据同理:按依赖顺序 deleteMany 子表再删父表,用 $transaction 包住保证原子。生产环境千万别这么干,用迁移和受控脚本。
硬删还是软删
硬删除直接删行,释放存储,但不可恢复。软删除给记录加 deletedAt 字段,删除只是更新标记:
// 软删除 = 更新标记
await prisma.user.update({
where: { id: 1 },
data: { deletedAt: new Date() },
});
两者取舍:
| 维度 | 硬删除 | 软删除 |
|---|---|---|
| 存储 | 立即释放 | 持续占用 |
| 恢复 | 不可恢复 | 置空 deletedAt 即可 |
| 查询 | 不用管 | 每处都要过滤 deletedAt: null |
软删可恢复、可审计,适合用户和业务数据;代价是每处查询都要记得带 deletedAt: null 过滤。这个过滤条件可以用客户端扩展(Client Extensions)统一注入,第 36 章细讲。硬删适合会话、日志这类过期即弃的数据。
参考来源
- Prisma 官方文档:CRUD(Update / Delete)
- Mapagam:Working with Update Operations / Working with Delete Operations
- Generalist Programmer:Prisma ORM Tutorial(Update / Delete Operations)