用 database/sql 连 postgresql 必须配 pgx 驱动(lib/pq 已归档),导入 _ "github.com/jackc/pgx/v5",连接串加 sslmode=disable;scan 用 sql.nullstring 处理 null;事务出错须立即 rollback;批量插入用 pgx.copyfrom 分批并严格匹配类型。

用 database/sql 连 PostgreSQL,别碰 pgx 就行?
不是。用 database/sql 没问题,但必须配对使用 pgx 或 lib/pq 驱动——database/sql 本身不带 PostgreSQL 实现。现在 lib/pq 已归档,官方推荐 pgx(尤其是 pgx/v5),它兼容 database/sql 接口,还能直连用原生协议。
常见错误现象:sql.Open("postgres", "...") 报错 sql: unknown driver "postgres"——因为没导入驱动包。
- 必须显式导入驱动:
import _ "github.com/jackc/pgx/v5/pgxpool"(用连接池)或import _ "github.com/jackc/pgx/v5"(用database/sql兼容层) - 连接字符串格式要对:
postgres://user:pass@localhost:5432/dbname?sslmode=disable;sslmode=disable在本地开发时必须显式加,否则默认尝试 SSL,连不上就卡住或报failed to connect: server does not support SSL -
pgxpool是首选,不是pgx.Conn:前者自动管理连接生命周期、复用、超时,后者需手动Close(),漏关会泄漏连接
Scan 时 panic: sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *string
这是典型类型不匹配:PostgreSQL 返回的是字节数组([]byte),而你试图扫进 *string 或未初始化的 string 变量。根本原因是 sql.NullString 或指针没用对,或者字段允许 NULL 但用了非指针基础类型。
- 查出来的列可能为 NULL?一律用
sql.NullString、sql.NullInt64等,别用*string——后者在值为 NULL 时会得到 nil 指针,Scan不认 - 如果确定非空,且字段是
TEXT或VARCHAR,直接用&myStr(string变量地址),不是&myStrPtr(*string的地址) - 更稳妥的做法:用结构体 +
pgx的Row.ToStructByName(需 struct tag 如pgx:"name"),它自动处理 NULL 和类型转换,比原生Scan少踩坑
事务里执行多条语句,为什么第二条就报 pq: current transaction is aborted, commands ignored until end of transaction block?
PostgreSQL 的事务强一致性机制:只要事务中某条语句出错(比如唯一键冲突、类型转换失败),整个事务就进入“aborted”状态,后续语句全被拒绝,直到你执行 ROLLBACK。
- Go 里用
tx, err := db.Begin()后,**每条tx.Query/Exec都必须检查err != nil**;一旦出错,立刻tx.Rollback(),不能继续往下跑 - 别指望用
defer tx.Rollback()保底——如果前面成功、最后才失败,defer 会把成功事务也回滚掉 - 想“忽略”某条失败语句继续执行?不行。PostgreSQL 不支持 Oracle 那种
SAVEPOINT自动跳过(除非你手动SAVEPOINT+ROLLBACK TO,但 Go 标准库不封装这个,得拼 SQL)
批量插入 10 万行,用 Exec 循环太慢,pgx.CopyFrom 怎么写才不崩?
CopyFrom 是 PostgreSQL 的二进制 COPY 协议接口,比逐条 INSERT 快 10–100 倍,但它对数据格式敏感,错一点就 panic 或静默丢数据。
- 目标表字段顺序、类型必须和
CopyFrom传入的[]string列名、[]interface{}数据严格一致;建议用pgx.Identifier构造表名,避免大小写或 schema 错误 - NULL 值必须传
nil,不能传""或0;字符串字段若含 \0 字符,会截断——先用bytes.ReplaceAll(data, []byte{0}, []byte{})清洗 - 别一次性塞 10 万行:分批,每批 1000–5000 行。太大内存吃紧,网络中断会整批失败;用
pgx.Batch或循环调CopyFrom都可以,关键是控制单次载荷
复杂点在于 COPY 协议不走 SQL 解析器,字段类型推导全靠你保证。少一个 nil,或多一个 time.Time 传成字符串,它不会报“类型错”,而是直接断连或写入乱码。这种地方没法靠日志猜,得拿小数据集先对齐结构再放大。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











