当前位置:首页 > 文章列表 > Golang > Go教程 > sqlNull类型处理可空列

sqlNull类型处理可空列

来源:17golang原创 2026-11-15 06:12:10 0浏览 收藏

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 把它们统一为一个值,就会丢失这份信息。若业务本来只需要展示兜底文本,则可以有意识地合并它们。

列值接收状态业务含义示例
NULLValid=false尚未设置
空字符串V="",Valid=true已设置为空
普通文本V 为文本,Valid=true已设置内容
SQL可空列与Null值和有效标记的静态关系
图1:可空值结构说明图。列值与 V、Valid 共同构成的状态相对应;图中的关系线不表示执行顺序。

迁移读取代码并处理无行和转换错误

下面的数据访问函数采用 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。

数据库可空类型和业务接口模型的职责结构
图2:接口职责结构说明图。Scan 与读取契约相关,Value 与参数契约相关;API 模型应独立决定对外空值表达。

数据库模型也不宜直接作为对外 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。选择可空类型后,最关键的动作仍是保留“值是否存在”这份信息,并在数据库层和接口层分别解释它。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go binary.PutVarint 预分配缓冲区与负数编码边界Go binary.PutVarint 预分配缓冲区与负数编码边界
上一篇
Go binary.PutVarint 预分配缓冲区与负数编码边界
embed嵌入模板与静态资源
下一篇
embed嵌入模板与静态资源
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1034次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1032次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1175次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1139次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    979次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码