批量更新单字段用sqlx.In+Rebind最简安全;多字段需VALUES+JOIN或临时表;pgx Batch可提升PostgreSQL吞吐,单批≤1000条并显式检查结果。

Go 用 sqlx 的 MustExec + IN 子句批量更新单字段
单字段、等值更新最简单也最安全,比如把一批用户状态设为已激活。直接拼 IN 列表即可,但注意参数数量限制和 SQL 注入风险。
- PostgreSQL/MySQL 默认支持最多 65535 个占位符,但实际应控制在 1000 以内,避免网络包超限或执行计划失效
- 必须用
sqlx.In配合sqlx.Rebind处理不同驱动的占位符(?vs$1),不能手拼字符串 - 示例:
query, args, _ := sqlx.In("UPDATE users SET status = ? WHERE id IN (?)", "active", ids) query = db.Rebind(query) db.MustExec(query, args...)
多字段批量更新必须用 VALUES + JOIN 或临时表
想给每条记录设不同的 name 和 score?IN 语法无能为力。主流方案是构造带序号的 VALUES 列表,再用 JOIN 关联更新。
- MySQL 8.0+ 支持
INSERT ... ON DUPLICATE KEY UPDATE,但要求主键/唯一键冲突,且语义是“插入或更新”,不是纯更新 - PostgreSQL 推荐
WITH t AS (VALUES (...)) UPDATE ... FROM t WHERE ...,VALUES行数建议 ≤ 5000,否则 CTE 性能下降明显 - SQLite 不支持
FROM子句更新,只能拆成事务内循环执行,或改用REPLACE INTO(需表有唯一约束)
用 pgx 批处理提升 PostgreSQL 批量更新吞吐量
当更新量超过 1 万行,原生 database/sql 的逐条准备 + 执行会成为瓶颈。pgx 的 Batch 能复用连接、减少 round-trip,实测快 3–5 倍。
- 不要把所有语句塞进一个
Batch:单个Batch最好 ≤ 1000 条,太大易 OOM;可分片提交 - 必须显式调用
batch.Exec()并检查返回的pgconn.BatchResults,否则错误会被静默吞掉 - 示例:
batch := &pgx.Batch{} for _, u := range users { batch.Queue("UPDATE users SET name = $1, score = $2 WHERE id = $3", u.Name, u.Score, u.ID) } br := conn.SendBatch(ctx, batch) for i := 0; i < len(users); i++ { _, err := br.Exec() if err != nil { /* 处理第 i 条失败 */ } }
事务边界和错误恢复容易被忽略
批量更新失败时,默认回滚全部——这没错,但若只想跳过坏数据继续执行,就得手动控制粒度。
立即学习“go语言免费学习笔记(深入)”;
- 别在事务里混用 DML 和 DDL(如
CREATE TEMP TABLE),某些驱动不支持嵌套事务,会报ERROR: current transaction is aborted - 用
SAVEPOINT实现局部回滚(仅 PostgreSQL):BEGIN; SAVEPOINT sp1; UPDATE ...; ROLLBACK TO sp1;,Go 中需自己拼 SQL 或用pgx的BeginTx控制 - 日志里记清每批的
ids范围和影响行数,出问题时能快速定位是数据脏还是逻辑错
批量更新真正的复杂点不在语法,而在你得清楚知道:数据库到底执行了几条语句、每条影响了多少行、失败时要不要重试、重试是否幂等。这些细节藏在驱动行为、SQL 方言和事务隔离级别底下,不碰真实数据量根本试不出来。


















