2014-01-14 38 views
8

我知道插入多个数据同时效率更高:golang - mysql一次插入多个数据?

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) 

我需要一个功能更安全,更高效地同时插入多个数据。

+0

我不知道如果MySQL支持这一点,但有些SQL实现支持传递数组作为参数传递给查询。如果MySQL支持它,并且你使用的任何数据库驱动程序也支持它,你可以做一些类似于db.Exec(“INSERT INTO test(n1,n2,n3)VALUES?,?,?”,[] int { 1,2,3},[] int {4,5,6},[] int {7,8,9})''。如果你想能够处理任意数量的插入(也就是说,你必须用“?”重复地构造一个查询),你仍然必须手动构造查询字符串,但是它比没有。 – joshlf

回答

30

为什么不是这样? (这里写未经测试所以有可能是语法错误):

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...) 
+2

同时修剪最后一个“,”,为什么len(sqlStr)-2但len(sqlStr)-1? –

+1

@DanielQiu len(sqlStr)-1是字符串的结尾索引,因为golang字符串是从零开始的。所以如果你想在最后修剪“,”你需要-2 –

+6

或'sqlStr = strings.TrimSuffix(sqlStr,“,”)' – huygn

-4

不是真正的问题的答案,因为它问,但无论如何…

你的“我不想这样做”代码片段很奇怪:你为什么要在循环中准备语句而不是之前?正确的做法将因此是:

data := []map[string]string{ 
    {"v1":"1", "v2":"1", "v3":"1"}, 
    {"v1":"2", "v2":"2", "v3":"2"}, 
    {"v1":"3", "v2":"3", "v3":"3"}, 
} 
stmt, _ := db.Prepare("INSERT INTO test(n1, n2, n3) VALUES(?, ?, ?)") 
defer stmt.Close() // in reality, you should check this call for error 
for _, v := range data { 
    res, _ := stmt.Exec(v["v1"], v["v2"], v["v3"]) 
} 

基本上比你原来的主张更好:你准备的发言恰好一次,然后调用它一次对输入数据的每一行。

我认为这种方法与VALUES子句后面带有多个元组的语句相比,不会有太多开销。唯一的缺点是,如果你想要所有三行都被原子地插入,你需要在事务内的循环中包含对stmt.Exec()的调用。

+0

我知道这是相当古老的,但是,我相信最初的问题是寻找将数据连接到单个sql查询,而不是运行连续查询(这是这样做的)。对于大型数据集,上述方法将效率低于将查询连接到一个大型查询中。 –