必须用sql.nullstring当数据库字段允许null且需区分“未填”(null)与“填空串”("");否则纯属自找麻烦。

sql.NullString 什么时候必须用,什么时候纯属自找麻烦
数据库字段允许为 NULL,但 Go 的 string 类型不能表示“不存在”,这是硬伤。不用 sql.NullString,查出来 NULL 会直接 panic 或静默变成空字符串,线上出问题时连日志都看不出是 NULL 导致的。sql.NullString 不是银弹——它只解决「读取时区分空字符串和 NULL」这一件事,写入、校验、API 返回还得自己兜底。
常见错误现象:sql: Scan error on column index 2: unsupported Scan, storing driver.Value type <nil> into type *string</nil>。这就是没用 sql.NullString 却碰上了 NULL 值。
- 适用场景:字段定义为
TEXT NULL或VARCHAR NULL,且业务逻辑需要明确知道“用户没填”(NULL)和“用户填了空字符串”("")的区别 - 不适用场景:字段加了
NOT NULL DEFAULT '',或者 API 层根本不关心 NULL/空串差异,那直接用string更干净 - 性能影响极小,但结构体字段变多(多了
Valid bool字段),JSON 序列化默认不会忽略无效值,得额外处理
Scan 之后必须检查 Valid,否则等于没用
sql.NullString 的核心不是类型,是那个 Valid 字段。很多人扫完就直接用 String 字段,忘了判 Valid,结果 NULL 被当成 "" 处理,bug 隐蔽又难复现。
示例对比:
立即学习“go语言免费学习笔记(深入)”;
var name sql.NullString
err := row.Scan(&name)
if err != nil {
return err
}
// ❌ 错误:没检查 Valid,name.String 可能是垃圾值
fmt.Println(name.String)
// ✅ 正确:先确认 Valid 才取值
if name.Valid {
fmt.Println("name:", name.String)
} else {
fmt.Println("name is NULL")
}
- 所有从 DB Scan 出来的
sql.NullString,只要后续逻辑依赖其值,就必须先判断Valid - 别在 struct tag 里加
json:",omitempty"试图自动过滤——Valid=false时String字段仍是空字符串,omitempty不生效 - 如果想让 JSON 输出为
null,得给 struct 实现MarshalJSON方法,或用第三方库如guregu/null
INSERT/UPDATE 时怎么传 NULL 或字符串
写库比读库更易错:Go 没有语法糖自动把 nil 转成 SQL NULL,你得手动构造 sql.NullString 实例。
正确姿势:
// 想插入 NULL → 设置 Valid=false
ns1 := sql.NullString{Valid: false}
// 想插入 "hello" → 设置 Valid=true + String 值
ns2 := sql.NullString{Valid: true, String: "hello"}
// 想插入 ""(空字符串)→ Valid 必须为 true,String 设为空
ns3 := sql.NullString{Valid: true, String: ""}
_, err := db.Exec("INSERT INTO users (name) VALUES (?)", ns1)
- 别用
sql.NullString{}默认零值——它的Valid是 false,String是 "",看起来像空串,其实是 NULL,极易误导 - 别把普通
string直接传给Exec参数 expectingsql.NullString,会编译报错 - ORM 如 GORM v2+ 支持原生
sql.NullString,但 v1 需要自定义 scanner,兼容性差
API 返回时别直接暴露 sql.NullString
HTTP 接口返回结构体里塞 sql.NullString,会导致 JSON 输出带 Valid 和 String 两个字段,前端看不懂,也不符合 REST 约定。
- 必须做一次转换:NULL → JSON
null,非 NULL → JSON 字符串 - 最简方案是在返回 struct 中用
*string:NULL 时设为nil,非 NULL 时指向实际字符串地址 - 如果字段可能为 ""(空串)且需与 NULL 区分,就得用自定义类型或额外字段标记状态,这时候
sql.NullString的语义优势才真正体现出来 - 别在 handler 里反复写
if ns.Valid { ... } else { ... },抽成一个ToStringPtr()工具函数更稳妥
NullString 的复杂点不在类型本身,而在它迫使你直面「缺失值」这个事实——而大多数业务代码,其实一直在假装它不存在。










