go语言中直接用db.query或db.exec传参即可实现预编译,但必须严格匹配驱动占位符(mysql用?、postgresql用$1、sqlite支持?和:name),参数需展开为独立interface{}值,in子句须动态生成占位符并逐个传参,禁止字符串拼接或整切片传递。

直接用 db.Query 或 db.Exec 传参即可,但必须用占位符 + 展开切片,不能拼字符串、不能传整块切片、不能忽略驱动差异。
占位符语法取决于数据库驱动,不是 database/sql 决定的
Go 的 database/sql 只是抽象层,实际占位符由驱动实现:MySQL 必须用 ?,PostgreSQL 必须用 $1、$2,SQLite 支持 ? 和 :name。写死一种会导致换库时报错,比如:
-
pq: syntax error at or near "$2"(在 MySQL 里用了 PostgreSQL 语法) -
sql: expected 2 arguments, got 1(传了[]interface{}{a,b}却没加...)
项目初期就该明确驱动,并在 SQL 字符串里严格匹配其占位符规则。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
IN 子句必须动态生成占位符,不能把切片当单个参数传
这是最常踩的坑:WHERE id IN (?) 绑定 []int{1,2,3},数据库收到的是 IN ('1,2,3'),不是三个独立值。
- 正确做法:按元素数量生成对应个数的
?,再把每个元素转成interface{}追加进参数切片 - 示例:
bValues := []int{1, 2, 3}; placeholders := strings.Repeat("?, ", len(bValues)-1) + "?"; query := "SELECT * FROM t WHERE id IN (" + placeholders + ")" - 参数必须展开:
db.Query(query, args...),args...缺一不可
参数必须是 interface{} 类型,且需显式处理 nil
虽然 Go 会自动装箱 int、string,但遇到 nil 就容易出错 —— nil 是 interface{} 的零值,但数据库字段可能需要 sql.NullString 或 sql.NullInt64 等类型。
- 所有参数统一转
interface{},避免隐式转换歧义 - 传
nil前确认目标字段是否允许 NULL;若需扫描 NULL,Scan时必须用sql.Null*类型 - 预编译语句(
db.Prepare)复用时,参数类型一致性比运行时检查更关键
真正难的不是写对一行 Query,而是让占位符数量、参数顺序、驱动语法、NULL 处理这四者在任意条件分支下都保持同步 —— 尤其是带可选 WHERE 条件或嵌套 IN 的查询,稍不注意就会在某个边缘 case 里崩掉。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










