sqlx.get() 是 sqlx.queryrow().structscan() 的封装,仅支持单行查询;queryrow() 需手动 scan 或 structscan,更灵活但繁琐;误用 get() 查多行会 panic。

sqlx.QueryRow() 和 sqlx.Get() 有什么区别?
本质是同一类操作,但 sqlx.Get() 是封装了 sqlx.QueryRow().StructScan() 的快捷方式,省去手动调用 StructScan。如果你结构体字段名和列名能对上(或已配好 db 标签),Get() 更简洁;否则 QueryRow() + 手动 Scan() 更可控。
常见错误:用 Get() 查询多行结果,直接 panic —— 它只接受单行,多行得用 Select()。
-
Get()内部会检查ErrNoRows并原样返回,需自己判断 - 若列名含大小写混用(如
user_name),结构体字段必须带db:"user_name"标签,否则StructScan失败且静默忽略字段 - 性能无差异,只是语法糖,底层都走
*sql.Rows
为什么 Scan() 总报 “sql: expected 2 destination arguments”?
这是参数数量不匹配的典型错误,发生在用 QueryRow().Scan() 时传入的变量地址个数 ≠ SQL 返回的列数。比如 SELECT id, name FROM users 返回两列,但你写了 row.Scan(&id),就会触发这个错误。
容易被忽略的是:哪怕某列是 NULL,它依然算一列,也得提供对应变量(类型要能接收 nil,比如 *string 或 sql.NullString)。
- 用
StructScan可避免数列数,但要求结构体字段与列一一映射(含db标签) - 临时调试可先用
rows.Columns()看实际列名和数量 - 别用
_占位,Scan()不支持忽略列
sqlx.In() 怎么安全拼接 IN 子句?
sqlx.In() 不是魔法函数,它只做两件事:生成带问号占位符的 SQL 字符串、把切片展开成查询参数。真正执行还得配合 sqlx.Rebind()(针对驱动)和 DB.Select() 等方法。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
最常踩的坑是忘记重绑定(尤其用 MySQL 驱动时)—— IN (?) 会被错译成 IN (?) 而非 IN (?, ?, ?),导致只查第一个值。
- MySQL 场景下,必须用
sqlx.Rebind(sqlx.BindType("mysql"), query)或直接用DB.Select()(它内部自动处理) - 切片为空时,
sqlx.In()返回"(?)", []interface{}{nil},多数数据库不认空 IN,得提前判断并改写逻辑(比如用WHERE 1=0) - 别手拼
IN (+ strings.Join(...),那是 SQL 注入温床
事务里嵌套 sqlx.NamedExec() 为什么没生效?
因为 sqlx.NamedExec() 默认走全局 *sqlx.DB,不感知你正在用的 *sqlx.Tx。必须显式调用事务对象的方法,比如 tx.NamedExec()。
另一个隐形问题是:如果事务中途 panic 或 return 未 Commit()/Rollback(),连接不会自动释放,可能卡住连接池。
- 务必用
defer tx.Rollback()开头,再在成功路径上tx.Commit()覆盖 -
NamedExec()的命名参数(:name)在事务中照常工作,无需额外配置 - 混合使用
Exec()和NamedExec()没问题,但注意前者用?占位,后者用:xxx,别混用
最麻烦的其实是错误链路:SQL 错误、Scan 类型错、事务未提交、空切片 IN、列名映射失败……它们往往不报明显错误,而是返回零值或静默截断。上线前最好用真实数据跑一遍边界 case。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










