sqlNull类型处理可空列
来源:17golang原创
时间:2026-11-15 06:12:10 371浏览 收藏
Go 读取 SQL 可空列时,可以把接收变量声明为 sql.Null[string]、sql.Null[int64] 等类型,将地址传给 Scan,先处理错误,再用 Valid 判断是否为 NULL。Valid 为 true 时才读取 V;空字符串、数字零和 false 都可能是有效值,不能代替 NULL 判断。
官方地址:https://pkg.go.dev/database/sql#Null
能力变化:统一为 sql.Null[T]
Go 1.22 引入了泛型 sql.Null[T]。它把已有 NullString、NullInt64 等类型的“值加有效标记”表达方式统一起来。这个变化适合减少数据访问层中可空字段的类型命名差异,并没有改变 SQL 中 NULL 的含义。这里讨论的是这项既有能力的采用方式,不是把它当作刚发布的新功能。
旧项目可以继续使用 sql.NullString。迁移后原来的 String 字段改为 V,Valid 的判定方式保持一致。T 虽然在类型声明里是 any,官方使用约定仍要求选择 driver.Value 接受的类型;常用的 string、int64、float64、bool、time.Time 等更容易与驱动值对应。不能据此把任意业务结构体当作可扫描列。
为什么必须同时保存值与有效标记
假设客户表有一个允许 NULL 的 nickname 列。“尚未设置昵称”可以对应 NULL,“明确设置为空字符串”可以对应空字符串。若业务需要区分二者,读取时使用 COALESCE 把它们统一为一个值,就会丢失这份信息。若业务本来只需要展示兜底文本,则可以有意识地合并它们。
| 列值 | 接收状态 | 业务含义示例 |
|---|---|---|
| NULL | Valid=false | 尚未设置 |
| 空字符串 | V="",Valid=true | 已设置为空 |
| 普通文本 | V 为文本,Valid=true | 已设置内容 |

迁移读取代码并处理无行和转换错误
下面的数据访问函数采用 PostgreSQL 的 $1 占位符,并假设调用方已经建立连接池、注册驱动且准备好 customers 表。其他数据库应按驱动要求修改占位符。函数不创建或关闭调用方传入的连接池。
package repository
import (
"context"
"database/sql"
"errors"
"fmt"
)
// LoadNickname 保留“无此客户”和“客户昵称为 NULL”的区别。
func LoadNickname(ctx context.Context, db *sql.DB, id int64) (sql.Null[string], bool, error) {
var nickname sql.Null[string]
err := db.QueryRowContext(ctx,
"SELECT nickname FROM customers WHERE id = $1", id,
).Scan(&nickname)
if errors.Is(err, sql.ErrNoRows) {
return sql.Null[string]{}, false, nil
}
if err != nil {
// 包括查询失败和类型转换失败;出错时不消费扫描结果。
return sql.Null[string]{}, false, fmt.Errorf("读取昵称: %w", err)
}
return nickname, true, nil
}
返回的 bool 表示客户行是否存在。行存在且 nickname.Valid 为 false,才表示该行昵称列为 NULL。QueryRowContext 的查询错误会由 Scan 返回,因此错误处理应放在 Scan 后。SQL NULL 被可空类型成功接收,本身不是 ErrNoRows。
迁移旧代码时,除了将类型替换为 sql.Null[string],还要检查所有读取 String 字段的地方,以及结构体字面量的字段名。不要为了统一形式一次性修改稳定接口的所有字段。
写入空值并保留接口模型
sql.Null[T] 同时提供 Value 方法。Valid 为 false 时,它返回表示 SQL NULL 的 nil;Valid 为 true 时,则按该值对应的驱动转换规则提供参数。构造有效的空字符串时,要明确设置 Valid: true。只设置 V 而遗漏 Valid,仍会表示 NULL。
例如 sql.Null[string]{V: "", Valid: true} 表示有效空字符串,sql.Null[string]{} 表示空值。执行更新时应通过占位符绑定参数,并处理 ExecContext 返回的错误。参数类型不能代替数据库约束检查,NOT NULL 列仍不能接受 SQL NULL。

数据库模型也不宜直接作为对外 JSON 契约。默认 encoding/json 编码会看到导出的 V 与 Valid 字段,不会自动把无效状态变成一个 JSON null。可以在响应层映射为指针或编写明确的序列化逻辑。对外字段是否缺省、是否允许显式清空,是另外一层协议设计;仅用一个空值标记不能表达 PATCH 请求的全部三态语义。
用小样例理解三种状态
以下是无需数据库驱动的独立演示,用 Scan 方法接收 nil、空字符串和文本,再查看相应的 Value。它便于理解类型契约,不能替代目标数据库与驱动的集成检查。这里列出代码和预期判断,没有声称已在本机执行。
package main
import (
"database/sql"
"fmt"
"log"
)
func main() {
// nil 对应 SQL NULL;后两项都是有效字符串。
for _, input := range []any{nil, "", "青禾"} {
var nickname sql.Null[string]
if err := nickname.Scan(input); err != nil {
log.Fatal(err)
}
value, err := nickname.Value()
if err != nil {
log.Fatal(err)
}
// 先看 Valid,再解释 V;Value 返回 nil 表示空值参数。
fmt.Printf("Valid=%t V=%q parameter=%#v\n",
nickname.Valid, nickname.V, value)
}
}
按类型契约,三项的 Valid 分别应为 false、true、true;第一项的参数值为 nil,第二项仍为空字符串。实际接入数据库时,还应关注驱动的文本、日期和数值返回类型,以及列值无法转换时的错误处理。
已经使用 NullString,必须迁移吗?
没有这个必要。工具链低于 Go 1.22 时不能采用泛型 Null。满足版本条件后,可以在新代码或局部重构中逐步使用,保留既有业务语义比统一拼写更重要。
Valid 能判断 SQL 查询有没有结果吗?
不能。查询无行应判断 Scan 返回的 sql.ErrNoRows;Valid 描述的是成功接收后某个列值是否为 NULL。
数值为零时应当把 Valid 改为 false 吗?
应当由业务输入是否为空决定。有效的零金额、零数量或者 false 都可以与 Valid=true 同时存在。
延伸阅读:https://go.dev/doc/go1.22;接口与类型约定:https://pkg.go.dev/database/sql#Null。选择可空类型后,最关键的动作仍是保留“值是否存在”这份信息,并在数据库层和接口层分别解释它。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
282 收藏
-
309 收藏
-
500 收藏
-
488 收藏
-
318 收藏
-
135 收藏
-
202 收藏
-
479 收藏
-
453 收藏
-
141 收藏
-
281 收藏
-
284 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习