拼接 WHERE 条件时必须避免 fmt.Sprintf 字符串拼接,应使用 sqlx.NamedQuery 配合 map 动态构造命名占位符(如 :name),或用 sqlx.In + Rebind 处理 IN 查询;IN 不能直接传 slice,需展开占位符;Struct 字段须用 db tag 显式映射列名,否则静默为零值。

拼接 WHERE 条件时别用 fmt.Sprintf 拼字符串
直接用 fmt.Sprintf 拼 SQL 是最常见也最危险的写法,哪怕参数来自内部配置或固定枚举,只要未来可能扩展为用户输入,就等于埋下 SQL 注入漏洞。sqlx 本身不阻止你这么做,但它提供的安全机制(如命名参数、NamedQuery)是明确要求你绕开字符串拼接的。
正确做法是用 map 或 struct 构造动态条件,再交给 sqlx 的 NamedQuery 或 Rebind 处理:
query := "SELECT * FROM users WHERE 1=1"
args := make(map[string]interface{})
if name != "" {
query += " AND name = :name"
args["name"] = name
}
if age > 0 {
query += " AND age > :age"
args["age"] = age
}
rows, err := db.NamedQuery(query, args)注意::name 这种命名占位符必须配合 NamedQuery 使用;若用 Query,需先调 sqlx.Rebind 转成目标驱动的占位符(如 $1 或 ?)。
IN 查询不能直接传 slice 给 NamedQuery
这是 sqlx 动态查询里最常踩的坑:把 []int{1,2,3} 直接塞进 map[string]interface{}{"ids": ids},然后写 WHERE id IN :ids,结果报错或查不到数据——因为 sqlx 不会自动展开 slice,它只会把整个 slice 当作一个值绑定。
立即学习“go语言免费学习笔记(深入)”;
解决方法只有两种:
- 手动展开成多个命名参数:
WHERE id IN (:id1, :id2, :id3),再逐个赋值 - 用
sqlx.In辅助函数生成问号占位符 +sqlx.Rebind:
ids := []int{1, 2, 3}
query, args, _ := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
query = db.Rebind(query) // 转成 $1,$2,$3 或 ?,取决于驱动
rows, _ := db.Queryx(query, args...)注意:sqlx.In 返回的是原始问号形式,必须过 Rebind 才能用于实际查询;且 args 是 slice,要展开为 args... 传入。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
Struct 绑定字段名和数据库列名不一致怎么办
sqlx 默认按 struct 字段名(大驼峰)匹配列名(snake_case),比如 UserName 字段默认找 username 列,但很多项目用 User_Name 或 user_name,不显式声明就会查出零值。
必须用 struct tag 显式指定映射关系:
type User struct {
ID int `db:"id"`
Name string `db:"user_name"`
Email string `db:"email_addr"`
}所有字段都得标,哪怕只有一两个不一致;漏标会导致对应字段始终为零值,且无任何错误提示。另外,tag 值区分大小写,db:"USER_NAME" 和 db:"user_name" 是不同列。
sqlx.Select 与 Queryx 在扫描行为上的关键差异
sqlx.Select 会自动将结果扫描进 slice of struct,而 Queryx 只返回 *sqlx.Rows,需要手动 Scan。但很多人忽略一点:如果 struct 字段 tag 写错或列名不存在,Select 会静默跳过该字段(填零值),而 Queryx + Scan 在字段数不匹配时直接 panic。
所以:
- 用
Select时务必核对所有dbtag,尤其新增字段后容易漏加 - 调试阶段可临时换用
Queryx+ 手动Scan,能更快暴露映射问题 -
Select内部仍调用Queryx,性能差异可忽略,选哪个纯看开发习惯和错误容忍度
动态构建查询最难的不是语法,而是边界条件——空条件、空 slice、NULL 列、字段名大小写混用,这些地方一漏,运行时才暴露,还不好复现。

















