【问题标题】:Bulk INSERT in Postgres in GO using pgx使用 pgx 在 GO 中的 Postgres 中批量插入
【发布时间】:2022-01-23 23:04:03
【问题描述】:

我正在尝试在 db 中批量插入密钥,这里是代码 关键结构

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)
}

在运行时抛出错误:expected 10 arguments, got 1

什么是批量插入的正确方法。

【问题讨论】:

    标签: postgresql go pgx


    【解决方案1】:

    您正在手工制作 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 复制协议来执行批量数据 插入。它返回复制的行数和错误。

    example usage 复制:

    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 它可能是 CopyFrom 的 PK 违规,具体取决于表结构。如果是您的情况,则在会话中创建临时表,在那里进行批量插入,然后执行select to maintable from temp on conflict do nothing。详见:stackoverflow.com/questions/13947327/…
    【解决方案2】:

    使用批处理 (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 的回答是否有任何性能影响。
    • 是的。它比 CopyFrom 慢。
    • Batch 比 CopyFrom 慢。但使用情况取决于业务案例。如果您对多个表有不同的插入,那么使用批处理可能是有意义的。如果您需要最大插入率到单个表中,那么当然是 CopyFrom。
    猜你喜欢
    • 1970-01-01
    • 2014-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 2019-07-15
    • 2021-02-17
    相关资源
    最近更新 更多