能否在GORM中仅对变更行执行Upsert操作?
GORM实现仅数据变更时执行Upsert的方案
问题描述
我目前用GORM执行Upsert的代码是这样的:
result := db.orm.Clauses(clause.OnConflict{ UpdateAll: true, }).Create(&row)
但我希望仅当数据实际发生变更时才执行Upsert,这样能保证只有数据不同时才更新updated_at时间戳。对应的SQL实现如下:
INSERT INTO X ( A, B, C ) VALUES ( $1, $2, $3 ) ON CONFLICT (A) DO UPDATE SET B = $2, C = $3, UpdatedAt = NOW() WHERE (X.A, X.B, X.C) IS DISTINCT FROM ($1, $2, $3);
请问能否通过GORM添加这个WHERE子句?
解决方案
当然可以实现,GORM支持自定义OnConflict子句的细节来匹配需求,具体实现如下:
示例代码
result := db.orm.Clauses(clause.OnConflict{ // 指定冲突的唯一约束列,对应SQL的ON CONFLICT (A) Columns: []clause.Column{{Name: "A"}}, // 定义冲突时要更新的字段 DoUpdates: clause.Assignments(map[string]interface{}{ "B": clause.CurrentColumn, // 引用插入语句中的B值,等价于SQL的EXCLUDED.B "C": clause.CurrentColumn, // 引用插入语句中的C值,等价于SQL的EXCLUDED.C "updated_at": clause.Expr{SQL: "NOW()"}, }), // 添加自定义WHERE条件,判断新旧数据是否存在差异 Where: clause.Expr{ SQL: "(X.A, X.B, X.C) IS DISTINCT FROM (EXCLUDED.A, EXCLUDED.B, EXCLUDED.C)", }, }).Create(&row)
关键说明
Columns:明确指定触发冲突的唯一键列,和SQL中的ON CONFLICT (A)对应clause.CurrentColumn:GORM提供的语法,用来引用插入语句中的待插入值,等价于SQL标准中的EXCLUDED关键字,避免手动重复写参数Where字段:直接传入自定义的SQL条件,通过IS DISTINCT FROM判断原有数据和新插入数据是否存在差异,只有满足条件时才会执行更新操作并修改updated_at
执行上述代码后,GORM生成的SQL逻辑会和你需求的完全一致,仅在数据实际变更时才触发Upsert的更新操作。
内容的提问来源于stack exchange,提问作者simonhorlick
相关产品推荐
相关产品推荐

