删除单条记录必须指定主键或WHERE条件,否则可能误删;软删除时Delete转为UPDATE,物理删除需Unscoped();批量删除必须显式Where,否则报错。

删除单条记录时,必须明确指定主键或条件
GORM 不支持无条件的单行删除(比如 Delete(&user) 且 user.ID 为零值),否则会触发全表删除——这是生产事故高发点。真正安全的做法是确保结构体实例的主键字段有有效值,或显式传入 WHERE 条件。
常见错误现象:DELETE FROM users WHERE id = 0 被执行,结果删掉 ID 为 0 的记录(如果存在);更危险的是,若用 Delete(nil) 或空结构体且未加 Where,GORM v2 默认会拒绝操作,但 v1 可能误删全表。
- 推荐写法:
db.Where("id = ?", 123).Delete(&User{})—— 不依赖结构体字段值,只靠Where控制范围 - 结构体方式:
db.Delete(&User{ID: 123})—— 要求ID是定义的主键字段,且非零值 - 避免写法:
db.Delete(&User{})(无 Where、无有效主键值)、db.Unscoped().Delete(&User{ID: 0})(绕过软删除还可能误匹配)
软删除字段(DeletedAt)存在时,Delete 实际执行 UPDATE
GORM 检测到模型包含 gorm.DeletedAt 类型字段(如 DeletedAt time.Time)时,Delete 默认转为软删除:不是 DELETE FROM,而是 UPDATE ... SET deleted_at = NOW()。这会影响你对“是否真删了”的判断。
使用场景:你需要物理删除(比如清理归档数据、测试重置),就必须绕过软删除逻辑。
- 物理删除单条:
db.Unscoped().Where("id = ?", 123).Delete(&User{}) - 物理删除多条:
db.Unscoped().Where("status = ?", "inactive").Delete(&User{}) - 注意:
Unscoped()会同时跳过所有作用域(scopes),包括你自己定义的默认查询过滤,需谨慎评估影响
批量删除必须显式加 Where,否则报错或被拒绝
GORM v2 默认禁止无条件批量删除(即不带 Where 的 Delete),会直接返回 ErrMissingWhereClause 错误。这不是限制,而是强制你确认意图。
错误信息示例:ERROR: missing where clause in delete statement
- 合法批量删:
db.Where("created_at - 若真要删全表(极少见),必须两次确认:
db.Unscoped().Where("1 = 1").Delete(&User{})(仍需Where) - 不要依赖
Limit()替代Where,db.Limit(100).Delete(&User{})依然报错
删除后获取影响行数,别只信返回 error
Delete 方法返回 *gorm.DB,错误只表示 SQL 执行失败(如连接中断、语法错),不反映是否真删到了数据。0 行影响在业务上常意味着“找不到目标”,但 GORM 不报错。
性能影响:多次删除同一条件时,没命中记录不会报错,但可能掩盖逻辑问题(比如 ID 输错、状态已变)。
- 检查影响行数:
result := db.Where("id = ?", 999).Delete(&User{}); if result.RowsAffected == 0 { /* 未找到 */ } -
RowsAffected是int64,注意类型比较(别用== nil) - 事务中删除后立刻查,比依赖
RowsAffected更可靠(尤其涉及触发器或外键级联)
实际删数据时,最易忽略的是软删除的隐式行为和 RowsAffected 的语义——它不保证业务逻辑成功,只保证 SQL 层执行了且没出错。


















