sql.NullString 是带 Valid 字段的结构体,非普通字符串;必须用地址扫描,Valid 为 true 时 String 才有效,JSON 输出需转换,不可与 sql.NullInt64 等混用。

sql.NullString 不是字符串,是结构体
很多人一看到 sql.NullString 就以为能当 string 直接用,结果 panic 或空指针。它本质是带 Valid bool 字段的结构体:
type NullString struct { String string; Valid bool }只有 Valid 为 true 时,String 才可信。
常见错误现象:panic: reflect: call of reflect.Value.SetString on zero Value(比如扫到 NULL 后直接赋值给未初始化的 sql.NullString 字段);或者忽略 Valid 判断,把 "" 当成业务上的空字符串。
- 扫描时必须用地址:用
&user.Name而不是user.Name,否则Valid永远不会被设为true - 写入数据库前要手动控制
Valid:想存 NULL 就设Valid = false;想存空字符串就设Valid = true且String = "" - 别在 JSON 输出里直接暴露
sql.NullString:它默认序列化成{"String":"xxx","Valid":true},业务接口一般要转成可读的string或null
Scan 时遇到 NULL,不处理就会丢数据
Go 的 database/sql 在扫描 NULL 到普通 string 时会报错:sql: Scan error on column index 0: unsupported Scan, storing driver.Value type <nil> into type *string。这是明确提醒你:不能跳过空值处理。
使用场景:查询用户资料,nickname 允许为空,但后端结构体字段声明成了 string。
立即学习“go语言免费学习笔记(深入)”;
- 要么改字段类型为
sql.NullString,并确保 Scan 传的是指针 - 要么用
sql.NullString做中间层,再转成业务需要的*string或string(比如if n.Valid { nickname = &n.String }) - 注意:如果表字段是
TEXT或VARCHAR,但允许 NULL,就一定得用sql.NullString或类似包装类型,没得绕
sql.NullInt64、sql.NullBool 等同理,但不能混用
sql.NullString 只解决字符串的 NULL;整数要用 sql.NullInt64,布尔用 sql.NullBool,时间用 sql.NullTime。它们都长得很像,但字段名、底层类型不同,强行互换会编译失败或运行时出错。
参数差异:sql.NullInt64 的 Int64 字段是 int64 类型,而 sql.NullString 的 String 是 string;两者 Valid 字段语义一致,但不能共用同一个变量接收。
- 别写
var n sql.NullString; err := row.Scan(&n)去扫一个INT字段——会 panic - PostgreSQL 的
JSONB或 MySQL 的JSON字段,即使允许 NULL,也不能用sql.NullString扫;得用*string或json.RawMessage - 自定义类型(如枚举)若可能为 NULL,建议封装自己的
NullEnum,而不是硬套sql.NullString
用 pointer to string 更轻量?但要注意零值陷阱
有人觉得 *string 比 sql.NullString 更简洁,确实:NULL → nil,非空 → 指向真实字符串。但它没内置 Valid 标识,容易误判。
性能影响:两者内存开销接近(*string 是 8 字节指针,sql.NullString 是 16 字节结构体),差别可忽略;但兼容性上,*string 无法直接用于 Scan,必须配合 sql.NullString 中转或手写 Scanner 接口。
- 用
*string接收时,必须先初始化为nil,且 Scan 时仍需借助sql.NullString:例如var ns sql.NullString; _ = row.Scan(&ns); if ns.Valid { s = &ns.String } -
*string的零值是nil,但业务中“字段没填”和“明确设为空字符串”可能语义不同——这时sql.NullString的Valid=false比*string=nil更明确 - ORM 如 GORM 默认用
*string处理可空字符串,但 raw SQL 场景下,sql.NullString是标准解法,别为了省几个字符放弃语义清晰性
最常被忽略的点:NULL 不等于空字符串,也不等于零值;Valid 字段不是装饰,是唯一可靠的判断依据。扫完不检查 Valid,就跟没处理 NULL 一样。










