利用Gorm批量更新DB数据

场景 1:批量更新多条不同数据

假设你有一批不同的数据项,需要根据主键(ID)逐条更新每个记录的不同字段值。这时可以利用批量更新的 SQL 语句,通过 gormUpdates() 方法实现批量更新。

示例代码:
func (r *LiveEnvDataItem) BatchUpdate(ctx context.Context, items []LiveEnvDataItem) error {
    orm, err := resource.GetGorm(resource.MySQLLiveQA)
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("get orm fail, err: %s", err))
        return err
    }

    tx := orm.Begin() // 开启事务
    for _, item := range items {
        err := tx.Model(&LiveEnvDataItem{}).Where("id = ?", item.ID).Updates(item).Error
        if err != nil {
            tx.Rollback()
            resource.LoggerService.Warning(ctx, fmt.Sprintf("update db data fail: err:%+v", err))
            return err
        }
    }

    if err := tx.Commit().Error; err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("commit transaction fail: err:%+v", err))
        return err
    }

    return nil
}
📝 说明:
  • 使用事务 tx := orm.Begin() 确保批量更新的原子性,避免部分更新成功、部分失败的情况。
  • 遍历 items,根据主键更新每条记录。
  • 如果任意一条更新失败,回滚事务。

场景 2:批量更新相同字段

如果你需要批量更新多条记录的相同字段值,可以直接使用 Where 条件结合 Updates() 方法来实现。

示例代码:
func (r *LiveEnvDataItem) BatchUpdateField(ctx context.Context, ids []int64, updateFields map[string]interface{}) error {
    orm, err := resource.GetGorm(resource.MySQLLiveQA)
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("get orm fail, err: %s", err))
        return err
    }

    err = orm.Model(&LiveEnvDataItem{}).Where("id IN ?", ids).Updates(updateFields).Error
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("update db datas fail: err:%+v", err))
        return err
    }

    return nil
}
📝 说明:
  • ids 是要更新的记录主键 ID 的列表。
  • updateFields 是要批量更新的字段和值的映射,例如:
updateFields := map[string]interface{}{
    "status": 1,
    "updated_at": time.Now(),
}

这样就能一次性更新多条记录的相同字段值。


场景 3:批量更新多条数据,不同字段值

如果你有多条记录,每条记录的字段值都不一样,可以构建批量更新的 SQL 查询。这种情况下,你需要用到 CASE 表达式 来动态构建 SQL。

示例代码:
func (r *LiveEnvDataItem) BatchUpdateDynamic(ctx context.Context, items []LiveEnvDataItem) error {
    orm, err := resource.GetGorm(resource.MySQLLiveQA)
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("get orm fail, err: %s", err))
        return err
    }

    sql := "UPDATE live_env_data_items SET status = CASE"
    ids := []int64{}

    for _, item := range items {
        sql += fmt.Sprintf(" WHEN id = %d THEN %d", item.ID, item.Status)
        ids = append(ids, item.ID)
    }
    sql += " END WHERE id IN ?"

    err = orm.Exec(sql, ids).Error
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("update db datas fail: err:%+v", err))
        return err
    }

    return nil
}
📝 说明:
  • 通过 CASE WHEN 动态构建批量更新 SQL。
  • ids 用于指定批量更新的记录范围。

场景 4:使用 GORM 的 Save() 方法批量更新

如果你的数据项包含主键,使用 Save() 方法也可以批量更新。

示例代码:
func (r *LiveEnvDataItem) BatchSave(ctx context.Context, items []LiveEnvDataItem) error {
    orm, err := resource.GetGorm(resource.MySQLLiveQA)
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("get orm fail, err: %s", err))
        return err
    }

    err = orm.Save(&items).Error
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("save db datas fail: err:%+v", err))
        return err
    }

    return nil
}
📝 说明:
  • Save() 会根据主键判断记录是否存在,存在则更新,不存在则插入。
  • 使用 Save() 方法时要注意性能问题,适合小批量更新。

对比总结

场景方法适用场景优点缺点
批量更新多条不同数据tx.Updates() + 事务每条记录的更新字段不同安全性高,支持事务遍历更新,性能一般
批量更新相同字段Where().Updates()批量更新相同字段值简单高效只适用于相同字段的更新
动态批量更新CASE WHEN + 动态 SQL每条记录的更新字段不同高效,SQL 一次性执行SQL 拼接复杂
批量保存Save()小批量更新或新增记录简单易用不适合大批量数据更新

最佳实践推荐

  • 小批量数据:使用事务 + Updates(),保证数据更新的原子性
  • 大批量相同字段更新:使用 Where().Updates()
  • 大批量不同字段更新:构建 CASE WHEN 动态 SQL,提升效率。
  • 新增或更新混合操作:使用 Save()

选择合适的方案,取决于你的业务需求和性能要求。如果数据量较大,建议尽量采用批量 SQL,减少数据库的多次交互,从而提升性能。

case then 怎么理解

你提到的这段代码是用来批量更新多条数据的不同字段值的,它动态生成了一条带有 CASE WHEN 表达式的 SQL 语句

下面我详细拆解这个 SQL 语句的含义,并解释为什么要用这种方式来批量更新。


💡 这段 SQL 的生成逻辑解释

sql := "UPDATE live_env_data_items SET status = CASE"
ids := []int64{}

for _, item := range items {
    sql += fmt.Sprintf(" WHEN id = %d THEN %d", item.ID, item.Status)
    ids = append(ids, item.ID)
}
sql += " END WHERE id IN ?"

最终生成的 SQL 示例

假设 items 包含以下数据:

IDStatus
12
23
34

则生成的 SQL 语句类似于:

UPDATE live_env_data_items 
SET status = CASE 
    WHEN id = 1 THEN 2 
    WHEN id = 2 THEN 3 
    WHEN id = 3 THEN 4 
END 
WHERE id IN (1, 2, 3);

SQL 语句分解

  1. UPDATE live_env_data_items

    • 表示要更新 live_env_data_items 表的数据。
  2. SET status = CASE

    • 表示要更新 status 字段,接下来的 CASE WHEN 表达式用于为每个 id 设置不同的 status 值。
  3. WHEN id = 1 THEN 2

    • 表示当 id 等于 1 时,将 status 字段更新为 2。
  4. WHEN id = 2 THEN 3

    • 表示当 id 等于 2 时,将 status 字段更新为 3。
  5. WHEN id = 3 THEN 4

    • 表示当 id 等于 3 时,将 status 字段更新为 4。
  6. END WHERE id IN (1, 2, 3)

    • 限定条件,表示只更新 id 为 1、2、3 的记录。

🎯 为什么要用 CASE WHEN 批量更新?

在实际业务中,批量更新多条数据时,可能每条记录的更新字段值不同,如果逐条执行 UPDATE 操作,性能很差。而 CASE WHEN 表达式允许你用一条 SQL 语句来实现批量更新,避免了多次执行 UPDATE,从而提升性能。

两种方式的对比
方式SQL 示例优缺点
逐条更新UPDATE live_env_data_items SET status = 2 WHERE id = 1;
UPDATE live_env_data_items SET status = 3 WHERE id = 2;
UPDATE live_env_data_items SET status = 4 WHERE id = 3;
缺点:需要执行多次 SQL,效率低。
CASE WHEN 批量更新UPDATE live_env_data_items SET status = CASE WHEN id = 1 THEN 2 WHEN id = 2 THEN 3 WHEN id = 3 THEN 4 END WHERE id IN (1, 2, 3);优点:一次性执行批量更新,性能更好。

适用场景

CASE WHEN 批量更新适用于每条记录的更新值不同的情况。例如:

  • 订单状态更新
    不同订单的状态更新为不同值。

  • 用户权限更新
    不同用户的权限级别更新为不同值。


💻 改进代码

可以优化一下 SQL 拼接逻辑,避免手动拼接字符串,改用更安全的占位符:

优化后的代码:
func (r *LiveEnvDataItem) BatchUpdateDynamic(ctx context.Context, items []LiveEnvDataItem) error {
    orm, err := resource.GetGorm(resource.MySQLLiveQA)
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("get orm fail, err: %s", err))
        return err
    }

    // 动态构建 CASE WHEN SQL
    sql := "UPDATE live_env_data_items SET status = CASE"
    var ids []int64
    var cases []string

    for _, item := range items {
        cases = append(cases, fmt.Sprintf("WHEN id = %d THEN %d", item.ID, item.Status))
        ids = append(ids, item.ID)
    }
    sql += " " + strings.Join(cases, " ") + " END WHERE id IN ?"

    // 执行 SQL
    err = orm.Exec(sql, ids).Error
    if err != nil {
        resource.LoggerService.Warning(ctx, fmt.Sprintf("update db datas fail: err:%+v", err))
        return err
    }

    return nil
}

🧩 总结

  • CASE WHEN 批量更新 可以用一条 SQL 语句更新多个不同记录的字段值,避免多次执行 UPDATE,从而提升数据库操作的效率。
  • 适用场景:订单状态更新、用户权限更新、配置项更新等。
  • 性能优势:批量更新时性能更好,减少数据库交互次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug 挖掘机

支持洋子

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值