sqlx查单行必须用Get而非QueryRow以启用StructScan;Get需传结构体地址和db tag,否则字段映射失败或静默置零;RETURNING须配Get/Select,命名参数仅PostgreSQL原生支持。

sqlx 不是 ORM,它只包装 database/sql,查单行必须用 Get(传结构体地址 + db tag),否则字段映射必然失败。
查单行必须用 Get,别碰 QueryRow
直接调 db.QueryRow 会跳过 sqlx 的 StructScan,哪怕 SQL 正确、连接正常,也大概率 panic 或字段全零值。
常见错误现象:sql: Scan error on column index 0: unsupported driver -> Scan pair: *string -> *interface{}——本质是没告诉 sqlx 目标类型。
-
Get内部调了QueryRow,但额外做了结构体反射扫描,必须传地址:db.Get(&user, query, args),不是db.Get(user, ...) - 结构体字段必须带
dbtag,比如数据库列是user_name,就得写UserName string `db:"user_name"`;默认小写匹配不可靠,别依赖 - SQL 里用了别名(如
SELECT id AS user_id),tag 就得是db:"user_id",否则扫不到 - 查不到数据时返回
sql.ErrNoRows,要用errors.Is(err, sql.ErrNoRows)单独判断,别和连接错误混在一起处理
RETURNING 必须配 Get 或 Select,Exec 拿不到返回值
PostgreSQL 的 INSERT ... RETURNING id 看似是插入语句,实则是带结果集的查询。用 Exec 调用不会报错,但返回值直接丢弃——这是最隐蔽的坑。
- 想取单个值(如
id):定义变量地址,var id int64; db.Get(&id, "INSERT ... RETURNING id", args) - 想取多列(如
id, created_at):定义结构体 +db.Get(&u, query, args),字段 tag 要和RETURNING列名或别名一致 - MySQL / SQLite 不支持
RETURNING,只能靠Result.LastInsertId();在 PG 里硬套会拿不到 ID
命名参数只在 PostgreSQL 原生支持,MySQL/SQLite 必须用 NamedQuery
裸写 :name 在 MySQL 驱动下会 panic:sql: expected 1 arguments, got 0。这不是配置问题,是驱动层不识别。
- 想用命名参数,必须走
db.NamedExec或db.NamedQuery,底层会做参数重绑定(rebind) - 传
map[string]interface{}时,键名必须和 SQL 中:xxx完全一致(大小写、下划线、拼写),少一个字符就失败 - 事务中
NamedExec报错,优先检查:map 键名是否匹配、有没有传nil、对应字段是否允许 NULL - 位置参数(
$1,?)始终安全,跨驱动通用;命名参数只是可读性优化,非必需
嵌入结构体扫描要显式加 prefix,不能靠匿名字段自动推导
sqlx.StructScan 不递归解析嵌入结构体字段。即使你写了 Author Author 匿名嵌入,SQL 返回 a.name 和 a.id,也不会自动塞进 Author.Name。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:SQL 列名带统一前缀(如
a_name,a_id),结构体字段用db:"prefix=a."声明 - 嵌入字段必须是匿名的(
Author,不是A Author),否则 prefix 不生效 - 如果 SQL 列名无法加前缀(比如 JOIN 多表且列名已固定),就别用嵌入,改用扁平结构体 + 手动赋值
RETURNING 是 PG 特性,但有人在 MySQL 连接里写它,表面不报错——因为 Exec 忽略了语法错误;又比如嵌入结构体没加 prefix,字段看似扫进去了,实际是覆盖或漏扫,上线后才暴露。这些都不是编译期能拦住的问题。


















