sqlx查单行必须用get而非queryrow,以启用structscan;exec仅用于无结果集的增删改,returning需配get/select;命名参数仅postgresql原生支持,mysql/sqlite需用位置参数或rebind。

SQLx 查询单行数据用 Get,别用 QueryRow
SQLx 封装了 database/sql,但直接调 QueryRow 会绕过 SQLx 的结构体自动扫描能力,导致字段映射失败或 panic。正确做法是统一走 SQLx 提供的 Get(查单行)或 Select(查多行)。
常见错误现象:sql: Scan error on column index 0: unsupported driver -> Scan pair: *string -> *interface{}——本质是没让 SQLx 知道目标结构体类型。
-
Get内部会调用QueryRow,但额外做了StructScan,必须传入地址(&user) - 结构体字段需有对应 tag,如
db:"id",否则按字段名全小写匹配列名 - 如果查不到数据,
Get返回sql.ErrNoRows,不是 nil
var user User err := db.Get(&user, "SELECT id, name FROM users WHERE id = $1", 123)
SQLx 执行原生 INSERT/UPDATE/DELETE 用 Exec,返回 sql.Result
和 database/sql 一样,增删改不返回结果集,只关心影响行数或最后插入 ID。SQLx 的 Exec 和标准库行为一致,但支持命名参数(需配合 NamedQuery)。
使用场景:需要获取自增 ID 或判断是否成功更新某条记录。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 普通位置参数用
Exec即可,比如INSERT INTO users(name) VALUES($1) - 想用
:name这类命名参数?得先用NamedQuery,否则报错sql: expected 1 arguments, got 0 -
Result.LastInsertId()在 SQLite/MySQL 下可用,PostgreSQL 不支持,得用RETURNING配合Get或Select
res, err := db.Exec("INSERT INTO users(name) VALUES($1)", "alice")
id, _ := res.LastInsertId() // MySQL/SQLite 有效,PG 不行
PostgreSQL 的 RETURNING 必须配 Get 或 Select,不能用 Exec
PostgreSQL 没有标准的 LastInsertId,惯用 RETURNING id 拿刚插入的值。但很多人误以为 Exec 能返回这些列——它不能,Exec 只处理无结果集语句。
容易踩的坑:写 INSERT ... RETURNING id 却用 Exec,结果 id 拿不到,还无报错。
- 带
RETURNING的语句本质是查询,必须用Get(单行)或Select(多行) - 结构体字段要和
RETURNING列名或别名匹配,比如RETURNING id AS user_id就得配db:"user_id" - 若只想要一个值(如 int),可直接扫到基础类型变量,但必须是地址:
var id int64; db.Get(&id, query, ...)
var id int64 err := db.Get(&id, "INSERT INTO users(name) VALUES($1) RETURNING id", "bob")
SQLx 命名参数在 MySQL 和 SQLite 下不生效,别盲目复制 PostgreSQL 写法
SQLx 的 NamedQuery 和 NamedExec 底层依赖驱动对 sql.Named 的支持。MySQL 驱动(go-sql-driver/mysql)和 SQLite 驱动(mattn/go-sqlite3)默认不解析 :name 或 @name,只认 ? 或位置参数。
现象:用 NamedExec("INSERT ... :name", map[string]interface{}{"name": "x"}) 在 MySQL 下报错 sql: expected 0 arguments, got 1。
- PostgreSQL(
lib/pq或jackc/pgx)原生支持命名参数 - MySQL/SQLite 下,要么改用位置参数(
?或$1),要么用Rebind手动转义:db.Rebind("INSERT ... :name")+db.Exec -
Rebind不是万能的——它只是字符串替换,不校验参数名是否存在,拼错就静默失败
// MySQL 安全写法
db.Exec("INSERT INTO users(name) VALUES(?)", "charlie")
// 或用 Rebind(仅限简单场景)
query := db.Rebind("INSERT INTO users(name) VALUES(:name)")
db.Exec(query, map[string]interface{}{"name": "charlie"})
SQLx 的核心价值在结构体自动映射和统一接口,但原生 SQL 的兼容性差异藏得很深——尤其是跨数据库时,RETURNING、命名参数、LastInsertId 这三处最容易出 silent bug。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










