Go database/sql 怎样依次读取多个 SQL 结果集
Go 的 database/sql 读取多个 SQL 结果集,关键是把“行的移动”和“结果集的切换”分开:当前结果集内部反复调用 rows.Next(),每次成功后再 rows.Scan();当前结果集读完后,再调用 rows.NextResultSet() 进入下一组。切换成功后,仍然必须先调用一次 Next(),不能直接 Scan()。
最稳妥的顺序是:外层负责结果集,内层负责行;内层结束后检查当前行迭代状态,外层结束后再检查是否有切换错误。某一组没有行不等于没有下一组结果集。
Next只负责把游标移动到当前结果集的下一行。NextResultSet只负责准备下一组结果集,返回false时要结合Rows.Err判断原因。- 多结果集通常有不同列结构,不能为了复用变量而强行用同一组
Scan参数。 - 查询得到的
Rows应及时关闭;迭代结束后仍要检查错误。

先分清 Next 和 NextResultSet 各自移动什么
*sql.Rows 可以看成一组按顺序到达的结果集。每个结果集内部又有自己的行游标。刚拿到 Rows 时,游标在第一组结果集的第一行之前,所以第一次读取也要先调用 Next。
| 方法 | 负责的层级 | 返回 false 时要关注什么 |
|---|---|---|
Next() | 当前结果集中的下一行 | 可能是当前组读完,也可能发生迭代错误,之后检查 Err() |
Scan(...) | 读取已经由 Next 准备好的当前行 | 目标变量数量和类型要匹配当前组的列 |
NextResultSet() | 下一组结果集 | 可能没有下一组,也可能切换时发生错误,之后检查 Err() |
官方文档还特别强调:调用 NextResultSet 后,扫描下一组前必须再次调用 Next;下一组结果集也可能完全没有行。因此,“上一组的 Next 返回 false”不能直接结束整个 Rows 的处理。
用两层循环依次读取全部结果集
如果各结果集的列结构相同,可以把每行先读成通用的 []any;如果列结构不同,则应在外层记录结果集编号,并为不同编号选择不同的 Scan 目标。下面的骨架只演示游标顺序,不绑定某个数据库的存储过程写法。
package main
import (
"context"
"database/sql"
"fmt"
)
// readAllResultSets 展示先读行、再切换结果集的固定顺序。
func readAllResultSets(ctx context.Context, db *sql.DB, query string) error {
rows, err := db.QueryContext(ctx, query)
if err != nil {
return fmt.Errorf("query multiple result sets: %w", err)
}
defer rows.Close() // 无论中途哪一步失败,都释放 Rows 关联的资源。
resultSetIndex := 0
for {
// 当前结果集可能为空;只有 Next 为 true 时才允许 Scan。
for rows.Next() {
var first, second any
if err := rows.Scan(&first, &second); err != nil {
return fmt.Errorf("scan result set %d: %w", resultSetIndex, err)
}
fmt.Printf("set=%d first=%v second=%v\n", resultSetIndex, first, second)
}
// Next 为 false 既可能是正常读完,也可能是迭代错误。
if err := rows.Err(); err != nil {
return fmt.Errorf("iterate result set %d: %w", resultSetIndex, err)
}
// 当前组读完后,才尝试准备下一组;返回 false 代表整个序列结束或切换失败。
if !rows.NextResultSet() {
if err := rows.Err(); err != nil {
return fmt.Errorf("advance after result set %d: %w", resultSetIndex, err)
}
break
}
resultSetIndex++
}
return nil
}
这段代码的两个判断不能合并成“Next 返回 false 就退出”:内层循环结束只说明当前结果集暂时没有下一行,外层还要给 NextResultSet 一次机会。反过来,NextResultSet 返回 false 后,也不能直接把它当作成功结束,必须先看 Rows.Err()。
空结果集和不同列结构怎么处理
一个结果集可以没有任何行,但后面仍然存在下一组结果集。此时内层 for rows.Next() 一次都不进入,随后只要 rows.Err() 没有错误,仍然应该继续调用 NextResultSet()。
更常见的边界是:第一组返回用户列,第二组返回统计列,两组列数和类型不同。这时可以按结果集编号分支,但仍保持相同的游标顺序:
// processCurrentSet 按结果集编号选择 Scan 目标,避免列结构错配。
func processCurrentSet(rows *sql.Rows, setIndex int) error {
for rows.Next() {
switch setIndex {
case 0:
var id int64
var name string
if err := rows.Scan(&id, &name); err != nil {
return fmt.Errorf("scan user set: %w", err)
}
fmt.Printf("user id=%d name=%s\n", id, name)
case 1:
var total int64
if err := rows.Scan(&total); err != nil {
return fmt.Errorf("scan summary set: %w", err)
}
fmt.Printf("total=%d\n", total)
default:
return fmt.Errorf("unexpected result set index %d", setIndex)
}
}
// 当前结果集为空时,这里仍然返回 nil,是否还有下一组由调用方决定。
return rows.Err()
}
如果结果集数量不是固定的,不能提前假设每一组都有两列。可以先读取 rows.Columns() 或 rows.ColumnTypes() 决定解析策略,但这些方法仍然只描述当前结果集;切换后需要重新读取列信息。

循环结束后检查 Err 和 Close
Rows.Err() 负责报告遍历过程中遇到的错误,也可以在显式或隐式 Close 后调用。实务中建议同时保留 defer rows.Close() 和关键位置的 Err 检查:前者兜底释放资源,后者让错误尽早带着结果集编号返回。
如果主动在中途停止消费,例如只需要第一组结果集,应明确调用 rows.Close(),不要继续调用 NextResultSet。如果完整消费到最后一组,驱动可能在没有更多结果集时自动关闭,但显式关闭仍然能让生命周期更清楚。
- 确认驱动本身支持多结果集,并确认查询确实返回了多组。
- 确认每次
Scan前都有一次成功的Next。 - 确认当前组结束后调用了
NextResultSet,而不是重新执行查询。 - 分别在当前组结束和切换失败处检查
Rows.Err()。 - 确认不同结果集的列结构没有复用错误的 Scan 参数。
常见问题
NextResultSet 返回 false 就一定是没有下一组吗?
不一定。它既可能表示没有更多结果集,也可能表示切换过程中发生错误。应立即检查 rows.Err() 来区分。
空结果集需要单独调用什么方法吗?
不需要。内层 Next 直接返回 false 后,先检查 Err,再继续调用 NextResultSet 即可。
能不能在 NextResultSet 后直接 Scan?
不能。切换到下一组后,必须先调用 Next,因为游标仍然位于该组第一行之前。
官方 API 文档在哪里?
可直接查看 https://pkg.go.dev/database/sql#Rows.NextResultSet,重点关注 Next、NextResultSet、Err 和 Close 四个方法的关系。
小结:把 Rows 想成“结果集容器中的行游标”即可:内层 Next 处理行,外层 NextResultSet 处理组,Scan 永远跟在 Next 后面,错误统一通过 Err 收口。这样即使遇到空组、异构列或驱动切换错误,代码的控制流也不会混乱。
栗子漫画常见问题怎么查?官方入口、公开资料页与反馈顺序说明
- 上一篇
- 栗子漫画常见问题怎么查?官方入口、公开资料页与反馈顺序说明
- 下一篇
- Go database/sql Scan 得到的 []byte 为什么不能直接长期保存
-
- Golang · Go教程 | 35分钟前 | 命令行 · go · 工程实践 · go generate Go代码生成 go generate -run 生成指令筛选
- go generate -run 怎样只执行指定生成指令
- 453浏览 收藏
-
- Golang · Go教程 | 39分钟前 | go · go test -json TestEvent Go测试事件 BuildEvent FailedBuild
- go test -json 输出怎样区分测试事件和构建事件
- 173浏览 收藏
-
- Golang · Go教程 | 48分钟前 | 基准测试 · testing · 性能 · Go教程 · GOMAXPROCS RunParallel Go testing.B.SetParallelism 并行基准 IO吞吐
- Go testing.B.SetParallelism 提高 IO 型并行基准吞吐的配置方法
- 404浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go testing.T.Cleanup 多次注册时为什么按逆序执行
- 309浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go flag.FlagSet 怎样让参数错误返回给调用方
- 273浏览 收藏
-
- Golang · Go教程 | 1小时前 | flag · go ·
- Go flag.BoolFunc 怎样处理重复出现的布尔开关
- 246浏览 收藏
-
- Golang · Go教程 | 1小时前 | 反射 · Go教程 · database/sql · Go database/sql Rows.Scan ColumnType ScanType 动态查询导出
- Go database/sql ColumnType.ScanType 怎样支持动态查询导出
- 381浏览 收藏
-
- Golang · Go教程 | 1小时前 | flag · go · Go flag.TextVar encoding.TextUnmarshaler
- Go flag.TextVar 怎样复用类型的文本解析逻辑
- 479浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · []byte · database/sql ·
- Go database/sql Scan 得到的 []byte 为什么不能直接长期保存
- 343浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go xml.Decoder.DecodeElement 怎样读取嵌套节点
- 436浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 446次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 517次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 532次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 478次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 303次使用
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- 接口返回的数据和数据库不一致怎么办?按数据生命周期排查
- 2026-06-27 398浏览
-
- Go语言操作redis数据库的方法
- 2023-01-07 214浏览
-
- Go单元测试对数据库CRUD进行Mock测试
- 2023-02-25 411浏览
-
- Beego中ORM操作各类数据库连接方式详细示例
- 2023-01-07 444浏览

