如何在Golang中使用预处理语句批量更新PostgreSQL多行数据
实现方案说明
可以实现该需求,这种写法完全兼容Go标准库database/sql的预处理逻辑,既可以保留单次数据库往返完成批量更新的性能优势,也能通过预处理避免SQL注入风险。
需要注意的是:因为待更新的行数不固定,VALUES部分的占位符需要动态生成,无法写死在预处理SQL模板中,具体实现逻辑如下:
实现步骤
- 首先把所有待更新的数据按参数顺序拍平存入
[]interface{}类型的vals切片 - 按数据行数动态生成VALUES对应的占位符串(PostgreSQL用
$N格式、MySQL用?格式即可) - 拼接完整的预处理SQL语句,执行
stmt.Exec(vals...)即可
代码示例(PostgreSQL语法)
import ( "database/sql" "fmt" "strings" _ "github.com/lib/pq" ) // 定义待更新的行结构 type UpdateItem struct { ColumnB string ColumnA int ColumnC string } func BatchUpdateTest(db *sql.DB, records []UpdateItem) (int64, error) { if len(records) == 0 { return 0, nil } // 构造参数切片和占位符切片 vals := make([]interface{}, 0, len(records)*3) placeholders := make([]string, 0, len(records)) for i, item := range records { baseIdx := i*3 + 1 // 生成每行的占位符:($1,$2,$3)、($4,$5,$6) 以此类推 placeholders = append(placeholders, fmt.Sprintf("($%d, $%d, $%d)", baseIdx, baseIdx+1, baseIdx+2)) // 参数按顺序加入vals vals = append(vals, item.ColumnB, item.ColumnA, item.ColumnC) } // 拼接完整预处理SQL sqlStr := fmt.Sprintf(` UPDATE test AS t SET column_a = c.column_a, column_c = c.column_c FROM (VALUES %s) AS c(column_b, column_a, column_c) WHERE c.column_b = t.column_b `, strings.Join(placeholders, ",")) // 预处理+执行 stmt, err := db.Prepare(sqlStr) if err != nil { return 0, err } defer stmt.Close() res, err := stmt.Exec(vals...) if err != nil { return 0, err } return res.RowsAffected() }
额外说明
- 如果使用MySQL数据库,只需要把占位符改成
?格式,同时把UPDATE语法调整为MySQL支持的多表更新写法即可,参数预处理的逻辑完全不变 - 该方案比循环执行单条更新性能提升非常明显,因为只有一次数据库网络往返,同时所有参数都走预处理转义,不存在SQL注入风险
内容的提问来源于stack exchange,提问作者user10714010
相关产品推荐
相关产品推荐

