
本文详解如何使用 lib/pq 驱动的 pq.Array 辅助函数,将 PostgreSQL 返回的数组(如 {H01,H02})正确扫描为 Go 原生切片(如 []string),避免因类型不匹配导致的程序挂起。
本文详解如何使用 lib/pq 驱动的 `pq.array` 辅助函数,将 postgresql 返回的数组(如 `{h01,h02}`)正确扫描为 go 原生切片(如 `[]string`),避免因类型不匹配导致的程序挂起。
在 Go 中操作 PostgreSQL 数组时,一个常见陷阱是:直接用 sql.Scan 将数据库返回的数组字符串(如 {H01,H02})赋值给 []string 变量——这会导致扫描阻塞(hang),既无 panic 也无错误日志,程序看似“冻结”。根本原因在于 lib/pq 默认不支持自动解析 PostgreSQL 数组格式;它期望接收符合内部二进制或文本协议规范的值,而裸切片指针无法满足该契约。
正确做法是借助 pq.Array 包装目标切片变量。该函数返回一个实现了 sql.Scanner 接口的代理值,能正确识别并反序列化 PostgreSQL 的数组表示(包括花括号包裹、逗号分隔、可选引号及转义字符等标准格式)。
以下是一个完整、可运行的示例:
package main
import (
"database/sql"
"log"
"os"
_ "github.com/lib/pq"
)
func main() {
// 示例连接字符串(请按实际环境修改)
connStr := os.Getenv("DATABASE_URL")
if connStr == "" {
connStr = "user=postgres dbname=test sslmode=disable"
}
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal("failed to connect:", err)
}
defer db.Close()
// 模拟原始问题中的查询:GROUP BY + ARRAY_AGG
query := `
SELECT id, ARRAY_AGG(code) AS codes
FROM (VALUES ('9252781', 'H01'), ('9252781', 'H02')) AS t(id, code)
WHERE id = '9252781'
GROUP BY id;
`
row := db.QueryRow(query)
var id string
var codes []string // 注意:此处声明为切片,但扫描时必须包装
// ✅ 正确:使用 pq.Array(&codes) 进行扫描
if err := row.Scan(&id, pq.Array(&codes)); err != nil {
log.Fatal("scan failed:", err)
}
log.Printf("ID: %s, Codes: %+v", id, codes) // 输出:ID: 9252781, Codes: [H01 H02]
}⚠️ 关键注意事项:
-
pq.Array必须传入变量地址(&slice),而非切片本身; - 目标切片类型需与 PostgreSQL 数组元素类型严格匹配(例如
text[]→[]string,integer[]→[]int); - 若数组可能为
NULL,建议先扫描为*[]string并判空,或使用sql.NullString等组合方式处理; -
pq.Array自动处理 PostgreSQL 数组语法细节(如"{\"a\",\"b\"}"或"{a,b}"),无需手动strings.Trim或正则解析; - 升级至
github.com/jackc/pgx/v5时,应改用pgx.InspectArray或pgx.ScanArg,因pgx原生支持数组,不再依赖pq.Array。
总结:pq.Array 是 lib/pq 生态中解析 PostgreSQL 数组的事实标准方案。跳过它而尝试手动解析不仅易出错,更会引发静默失败。始终确保扫描时显式包装,即可安全、稳定地在 Go 应用中消费数据库数组字段。

















