如何一次插入多个数据

时间:2014-01-14 07:32:08

标签: go

我知道一次性更高效地插入多个数据:

INSERT INTO test(n1, n2, n3) 
VALUES(v1, v2, v3),(v4, v5, v6),(v7, v8, v9);

如何在golang中做到这一点?

data := []map[string]string{
   {"v1":"1", "v2":"1", "v3":"1"},
   {"v1":"2", "v2":"2", "v3":"2"},
   {"v1":"3", "v2":"3", "v3":"3"},
}
//I do not want to do it
for _, v := range data {
    sqlStr := "INSERT INTO test(n1, n2, n3) VALUES(?, ?, ?)"
    stmt, _ := db.Prepare(sqlStr)
    res, _ := stmt.Exec(v["v1"], v["v2"], v["v3"])
}

使用字符串拼接,但它并不好。 db.Prepare更安全,对吧?

sqlStr := "INSERT INTO test(n1, n2, n3) VALUES"
for k, v := range data {
    if k == 0 {
        sqlStr += fmt.Sprintf("(%v, %v, %v)", v["v1"], v["v2"], v["v3"])
    } else {
        sqlStr += fmt.Sprintf(",(%v, %v, %v)", v["v1"], v["v2"], v["v3"])
    } 
}
res, _ := db.Exec(sqlStr)

我需要一个函数更安全,更有效地插入多个数据。

7 个答案:

答案 0 :(得分:47)

为什么不是这样的? (写这里没有测试,所以可能存在语法错误):

sqlStr := "INSERT INTO test(n1, n2, n3) VALUES "
vals := []interface{}{}

for _, row := range data {
    sqlStr += "(?, ?, ?),"
    vals = append(vals, row["v1"], row["v2"], row["v3"])
}
//trim the last ,
sqlStr = sqlStr[0:len(sqlStr)-2]
//prepare the statement
stmt, _ := db.Prepare(sqlStr)

//format all vals at once
res, _ := stmt.Exec(vals...)

答案 1 :(得分:3)

对于Postgres,lib pq支持批量插入:https://godoc.org/github.com/lib/pq#hdr-Bulk_imports

但是可以通过下面的代码实现相同的目的,但是真正有用的是当人们尝试执行批量条件更新(相应地更改查询)时。

要对Postgres执行类似的批量插入,可以使用以下功能。

// ReplaceSQL replaces the instance occurrence of any string pattern with an increasing $n based sequence
func ReplaceSQL(old, searchPattern string) string {
   tmpCount := strings.Count(old, searchPattern)
   for m := 1; m <= tmpCount; m++ {
      old = strings.Replace(old, searchPattern, "$"+strconv.Itoa(m), 1)
   }
   return old
}

因此上面的示例变为

sqlStr := "INSERT INTO test(n1, n2, n3) VALUES "
vals := []interface{}{}

for _, row := range data {
   sqlStr += "(?, ?, ?),"
   vals = append(vals, row["v1"], row["v2"], row["v3"])
}

//trim the last ,
sqlStr = strings.TrimSuffix(sqlStr, ",")

//Replacing ? with $n for postgres
sqlStr = ReplaceSQL(sqlStr, "?")

//prepare the statement
stmt, _ := db.Prepare(sqlStr)

//format all vals at once
res, _ := stmt.Exec(vals...)

答案 2 :(得分:1)

Gorm V2(于2020年8月30日发布)现在支持批量插入查询。

// Pass slice data to method Create, GORM will generate a single SQL statement
// to insert all the data and backfill primary key values,
// hook methods will be invoked too.

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
DB.Create(&users)

for _, user := range users {
  user.ID // 1,2,3
}

有关更多详细信息,请参见此处的官方文档:https://gorm.io/docs/create.html

答案 3 :(得分:0)

如果启用多条语句,则可以一次执行多条语句。 这样,您应该能够处理多个插入。

https://github.com/go-sql-driver/mysql#multistatements

答案 4 :(得分:0)

来自https://gorm.io/docs/create.html#Batch-Insert

代码示例:

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
DB.Create(&users)

答案 5 :(得分:0)

这是一种有效的过渡方式,仅在提交后才进行网络调用。

func insert(requestObj []models.User) (bool, error) {
    tx := db.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()

    for _, obj := range requestObj {
        if err := tx.Create(&obj).Error; err != nil {
            logging.AppLogger.Errorf("Failed to create user")
            tx.Rollback()
            return false, err
        }
    }
    err := tx.Commit().Error
    if err != nil {
        return false, err
    }
    return true, nil
}

答案 6 :(得分:-1)

经过广泛研究,这对我有用:

var values []interface{}
for _, scope := range scopes {
    values = append(values, scope.ID, scope.Code, scope.Description)
}
sqlStr := `INSERT INTO scopes (application_id, scope, description) VALUES %s`
sqlStr = setupBindVars(sqlStr, "(?, ?, ?)", len(scopes))

_, err = s.db.ExecContext(ctx, sqlStr, values...)

//替代助手功能?正确数量的绑定变量集

func setupBindVars(stmt, bindVars string, len int) string {
    bindVars += ","
    stmt = fmt.Sprintf(stmt, strings.Repeat(bindVars, len))
    return strings.TrimSuffix(stmt, ",")
}