使用pgx在GO中进行Postgres的批量插入

9

我在尝试使用Go语言批量插入数据库中的键,以下是代码:

键结构体
type tempKey struct {
keyVal  string
lastKey int

}

测试密钥

data := []tempKey{
    {keyVal: "abc", lastKey: 10},
    {keyVal: "dns", lastKey: 11},
    {keyVal: "qwe", lastKey: 12},
    {keyVal: "dss", lastKey: 13},
    {keyVal: "xcmk", lastKey: 14},
}

插入部分

dbUrl := "db url...."
conn, err := pgx.Connect(context.Background(), dbUrl)
if err != nil {
    println("Errrorr...")
}
defer conn.Close(context.Background())
sqlStr := "INSERT INTO keys (keyval,lastval) VALUES "
dollars := ""
vals := []interface{}{}
count := 1
for _, row := range data {
    dollars = fmt.Sprintf("%s($%d, $%d),", dollars, count, count+1)
    vals = append(vals, row.keyVal, row.lastKey)
    count += 2
}
sqlStr += dollars
sqlStr = sqlStr[0 : len(sqlStr)-1]
fmt.Printf("%s \n", sqlStr)

_, erro := conn.Exec(context.Background(), sqlStr, vals)
if erro != nil {
    fmt.Fprint(os.Stderr, "Error : \n", erro)
}

在运行时它会抛出错误:期望10个参数,但只传入了1个 批量插入的正确方式是什么?
2个回答

23
您正在手动编写SQL语句,这是可以的,但您没有利用pgx来帮助完成此操作(请参见下文)。对于大型输入,像这样附加到SQL字符串可能效率低下。
dollars = fmt.Sprintf("%s($%d, $%d),", dollars, count, count+1)

但是最后的值有一个尾随的,,相反你需要一个终止字符;来表示语句的结束。

顺便说一下,这个字符串截断代码行是冗余的:

sqlStr = sqlStr[0 : len(sqlStr)-1] // this is a NOOP

无论如何,最好使用更高效的工具,比如strings.Builder来构建长字符串。


pgx文档中,使用pgx.Conn.CopyFrom

func (c *Conn) CopyFrom(tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int, error)

CopyFrom使用PostgreSQL复制协议执行批量数据插入。它返回已复制的行数和一个错误。

Copy的示例用法:

rows := [][]interface{}{
    {"John", "Smith", int32(36)},
    {"Jane", "Doe", int32(29)},
}

copyCount, err := conn.CopyFrom(
    pgx.Identifier{"people"},
    []string{"first_name", "last_name", "age"},
    pgx.CopyFromRows(rows),
)

谢谢提供额外的信息......我之前不知道Copy,现在让它起作用了。 - AnswerRex
3
@AnswerRex,根据表格结构,在CopyFrom上可能会出现PK冲突。 如果是这种情况,请在会话中创建临时表格,进行批量插入,然后执行“select to maintable from temp on conflict do nothing”。详见:https://dev59.com/bmYr5IYBdhLWcg3wG2pI - serge-v

16

使用批处理 (https://github.com/jackc/pgx/blob/master/batch_test.go):

batch := &pgx.Batch{}
batch.Queue("insert into ledger(description, amount) values($1, $2)", "q1", 1)
batch.Queue("insert into ledger(description, amount) values($1, $2)", "q2", 2)
br := conn.SendBatch(context.Background(), batch)

谢谢……这很简单,而且像魅力一样运作良好,但我有疑问,这是否会受到@colm.anseo的回答中的性能影响。 - AnswerRex
比CopyFrom慢。 - serge-v
1
批处理比CopyFrom慢。但使用取决于业务情况。如果您需要将不同的插入操作插入到多个表中,则可能有意义使用批处理。如果您需要最大的单表插入速率,那么当然是CopyFrom。 - serge-v

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接