mysql存储过程返回结果集时必须用sql.db.query;无结果集用exec。多结果集需用nextresultset循环读取,out参数需通过@var用户变量中转,事务中调用要防隐式提交。

MySQL 存储过程返回结果集时,sql.DB.Query 才能拿到数据
Go 的 database/sql 包不区分“调用存储过程”和“执行普通 SQL”,关键看存储过程是否返回结果集。如果它用 SELECT 输出行,就必须用 Query;如果只是做 INSERT/UPDATE 或 SET 变量,用 Exec 就够了。
常见错误是看到 CALL proc_name() 就下意识用 Exec,结果发现 sql: expected statement, not query 或直接静默丢掉结果。
-
Query:适用于有SELECT输出的存储过程(哪怕只有一行一列) -
Exec:仅适用于无结果集的操作,比如INSERT INTO ...; SET @x = 1; - MySQL 驱动(如
go-sql-driver/mysql)要求:每个CALL后必须读完所有结果集,否则后续查询会报commands out of sync
多个结果集必须用 Rows.NextResultSet 循环读取
MySQL 存储过程可以返回多个 SELECT,比如先查用户,再查其订单。Go 默认只读第一个结果集,不手动切换就会漏掉后面的数据。
错误现象:Rows.Scan 只拿到第一组数据,第二组完全不可见,且不会报错。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 每次
rows.NextResultSet()返回true,就代表还有下一个结果集 - 每个结果集都要单独调用
rows.Columns()和循环rows.Scan() - 别忘了在最外层
for rows.Next()前先处理第一个结果集
rows, err := db.Query("CALL user_with_orders(?)", userID)
if err != nil {
return err
}
defer rows.Close()
// 第一个结果集
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
return err
}
fmt.Println("User:", name)
}
// 后续结果集
for rows.NextResultSet() {
for rows.Next() {
var orderID int
if err := rows.Scan(&orderID); err != nil {
return err
}
fmt.Println("Order:", orderID)
}
}
带 OUT/INOUT 参数的存储过程,得用 MySQL 用户变量临时中转
MySQL 存储过程的 OUT 参数不能被 Go 直接接收——database/sql 没提供绑定机制。必须靠 @var 用户变量 + 额外 SELECT @var 获取值。
典型错误:写 CALL proc(?, @out) 然后以为 @out 能自动映射到 Go 变量,实际根本拿不到。
- 先
db.Exec("SET @out = NULL")初始化变量 - 再
db.Exec("CALL my_proc(?, @out)", inValue) - 最后
db.QueryRow("SELECT @out").Scan(&outValue) - 注意:所有操作要在同一个连接上完成,所以建议用
db.Conn()显式控制连接生命周期,避免连接池打乱变量作用域
事务内调用存储过程,要小心隐式提交和 autocommit 行为
MySQL 在某些情况下会强制提交当前事务,比如存储过程中包含 DROP TABLE、ALTER TABLE 或显式 COMMIT。这时 Go 侧还在用 tx.Query,就会报 invalid connection 或结果错乱。
更隐蔽的问题:MySQL 默认 autocommit=1,但开启事务后,部分存储过程内部语句仍可能触发隐式提交(尤其 DDL)。
- 检查存储过程代码里有没有
CREATE/DROP/ALTER/TRUNCATE—— 这些大概率导致事务中断 - 用
SELECT @@autocommit和SELECT @@in_transaction在过程前后验证事务状态 - 若必须用含 DDL 的过程,考虑拆成两阶段:先在事务外准备结构,再在事务内调用纯 DML 的过程
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










