golang grom 实现 INSERT...ON DUPLICATE KEY UPDATE

发布时间 2023-09-30 20:46:31作者: Lucky小黄人^_^

内容来自对 chatgpt 的咨询

前言(INSERT...ON DUPLICATE KEY UPDATE 介绍)

当你在MySQL中插入数据,如果唯一键发生冲突,可以使用 INSERT...ON DUPLICATE KEY UPDATE 语句实现插入和更新操作。其基本语法如下:

INSERT INTO table (col1, col2, ...colN)
VALUES (value1, value2, ...valueN)
ON DUPLICATE KEY UPDATE
col1 = value1, col2 = value2, ...colN = valueN;

这条语句的逻辑是:如果插入的记录与表中现有记录的唯一索引或主键冲突,那么就执行 UPDATE 操作。如果没有冲突,那么就执行 INSERT 操作。

例如,表users有唯一键user_id,现在想插入或更新一条 user_id=1 的记录:

INSERT INTO users (user_id, user_name, user_email)
VALUES (1, 'John', 'john@example.com')
ON DUPLICATE KEY UPDATE
user_name='John', user_email = 'john@example.com';

在以上的例子中,如果 user_id=1 的这条记录已经存在,在执行此语句后,user_name 和 user_email 会被更新为新的值。如果 user_id=1 的记录不存在,那么这个语句就会新插入一条记录。

注意

在 ON DUPLICATE KEY UPDATE 后面的赋值表达式中,你不能使用 VALUES(col) 获取插入语句中的值,因为它可能被认为是值的字面量而不是列表的值。你应该直接使用要输入的值或者变量。

在 grom 中使用 mysql 的这种语法效果

可以使用GORM的 Exec() 函数,执行自定义的SQL语句来实现MySQL的 INSERT...ON DUPLICATE KEY UPDATE 功能。下面这段代码段会尝试插入users数组中的每个用户,并在发生主键冲突的情况下,更新冲突数据的user_name和user_email字段。需要注意的是,VALUES(user_name), VALUES(user_email)中的VALUES函数能引用到插入时的真实数据,占位符会被替换成真实数据。

users := []User{
    {UserID: 1, UserName: "John1", UserEmail: "john1@example.com"},
    {UserID: 2, UserName: "John2", UserEmail: "john2@example.com"},
    {UserID: 3, UserName: "John3", UserEmail: "john3@example.com"},
}

valueStrings := []string{}
valueArgs := []interface{}{}

// 构造插入语句和冲突时的更新语句
for _, user := range users {
    valueStrings = append(valueStrings, "(?, ?, ?)")
    valueArgs = append(valueArgs, user.UserID)
    valueArgs = append(valueArgs, user.UserName)
    valueArgs = append(valueArgs, user.UserEmail)
}

stmt := fmt.Sprintf("INSERT INTO users (user_id, user_name, user_email) VALUES %s"+
    "ON DUPLICATE KEY UPDATE user_name=VALUES(user_name), user_email=VALUES(user_email)",
    strings.Join(valueStrings, ","))

db.Exec(stmt, valueArgs...)

注意点

  • 注意使用这种方法时要确保你对用户的数据有100%的信任,或者已经进行了相应的清理,以防止SQL注入攻击。

  • 在执行 SQL 语句之前,需要检查 users 数组是否为空。原因是如果 users 长度为0,则跳过构造和执行 SQL 语句的步骤。如果 users 数组为空,那么在构造 SQL 插入语句的过程中,valueStrings 和 valueArgs 切片都将保持为空。这意味着 stmt 字符串将尝试插入空的值,形如:INSERT INTO users VALUES ON DUPLICATE KEY UPDATE,这将导致 SQL 语法错误,因为在 "VALUES" 关键字后并未提供任何数据。