【Golang】sql.Null* 范例使用(处置惩罚空值和零值)

[复制链接]
发表于 2025-12-30 06:37:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
 sql.NullString 和 sql.NullInt64 范例(以及其他类似的 sql.Null* 范例)在处置惩罚数据库利用时非常有用,尤其是在 Go 语言的 database/sql 包中。它们的告急用途包罗:

  •         体现 NULL 值:

    • 在数据库中,NULL 体现“没有值”。sql.Null* 范例允许你在 Go 语言中直接体现和处置惩罚这种“没有值”的状态,而不是使用 Go 的零值(如空字符串 "" 或整数 0)来体现 NULL,如许可以更精确地反映数据库中的数据状态。

  •         数据库交互:

    • 当你从数据库查询数据时,某些字段大概是 NULL。使用 sql.Null* 范例可以区分一个字段是真正包罗零值还是 NULL。这对于后续的逻辑处置惩罚非常告急,由于零值和 NULL 在逻辑上大概有差异的寄义。

  •         克制错误:

    • 假如不使用 sql.Null* 范例,你大概会错误地将数据库中的 NULL 值表明为 Go 语言中的零值,这大概导致逻辑错误或步调瓦解。

  •         简化代码:

    • 使用 sql.Null* 范例可以简化代码,由于你不必要编写额外的查抄来确定一个值是否来自数据库中的 NULL。Valid 字段可以直接告诉你这个值是否有用。

  •         数据完备性:

    • 在将数据写入数据库时,sql.Null* 范例可以资助你保持数据的完备性。你可以精确地标志哪些字段是故意设置为 NULL,哪些字段是具有实际值的。

  •         机动性:

    • sql.Null* 范例提供了机动性,允许你在不改变现有代码布局的环境下,处置惩罚那些大概为 NULL 的字段。

示例场景:

假设你正在处置惩罚一个用户信息表,此中有一个字段体现用户的出生日期。在某些环境下,用户大概没有提供出生日期,因此这个字段在数据库中是 NULL。使用 sql.NullString 或 sql.NullInt64(取决于出生日期怎样存储)可以让你在 Go 步调中精确地体现这种环境,并在必要时举行恰当的处置惩罚。
  1. package main
  2. import (
  3.         "database/sql"
  4.         "fmt"
  5. )
  6. // DbBackedUser 用户结构体,由数据库支持
  7. type DbBackedUser struct {
  8.         Name sql.NullString
  9.         Age  sql.NullInt64
  10. }
  11. func main() {
  12.         // 空值
  13.         var name sql.NullString
  14.         x := DbBackedUser{Name: name, Age: sql.NullInt64{Int64: 0, Valid: false}}
  15.         // 输出结果
  16.         fmt.Println("Name:")
  17.         fmt.Println("String:", x.Name.String) // 输出: ""
  18.         fmt.Println("Valid:", x.Name.Valid)   // 输出: false
  19.         fmt.Println("Age:")
  20.         fmt.Println("Int64:", x.Age.Int64)  // 输出: 0
  21.         fmt.Println("Valid:", x.Age.Valid)   // 输出: false
  22. }
复制代码
  1. Name:
  2. String:
  3. Valid: false
  4. Age:
  5. Int64: 0
  6. Valid: false
复制代码
到场与数据库交互
1. 创建测试表

clickhouse-client
起首,我们必要在 ClickHouse 中创建一个测试表。由于你的数据布局包罗 Name 和 Age,我们可以使用以下 SQL 语句创建表: 
  1. CREATE TABLE test_table
  2. (
  3.     `Name` Nullable(String),
  4.     `Age`  Nullable(Int64)
  5. ) ENGINE = Memory;
复制代码
2. 插入数据

接下来,我们插入一些数据,包罗空值和零值:
  1. INSERT INTO test_table VALUES ('Kimi', 30), ('', 0), (NULL, NULL);
复制代码
这里,我们插入了三行数据:

  • 第一行:Name 是 "Kimi",Age 是 30。
  • 第二行:Name 是空字符串,Age 是 0。
  • 第三行:Name 和 Age 都是 NULL。
3. 查询数据

查询表中的全部数据:
  1. SELECT * FROM test_table;
复制代码
  1. package main
  2. import (
  3.         "database/sql"
  4.         "fmt"
  5.         "log"
  6.         _ "github.com/ClickHouse/clickhouse-go"
  7. )
  8. // DbBackedUser 用户结构体,由数据库支持
  9. type DbBackedUser struct {
  10.         Name sql.NullString
  11.         Age  sql.NullInt64
  12. }
  13. func main() {
  14.         // 连接到 ClickHouse 数据库
  15.         connStr := "tcp://localhost:9000?username=default&password=&database=default"
  16.         db, err := sql.Open("clickhouse", connStr)
  17.         if err != nil {
  18.                 log.Fatal(err)
  19.         }
  20.         defer db.Close()
  21.         // 查询并打印现有的数据
  22.         fmt.Println("Query results before insertion:")
  23.         queryData(db)
  24.         fmt.Println("查询并打印现有的数据")
  25.         // 开始事务
  26.         tx, err := db.Begin()
  27.         if err != nil {
  28.                 log.Fatal(err)
  29.         }
  30.         // 空值
  31.         var name sql.NullString
  32.         x := DbBackedUser{Name: name, Age: sql.NullInt64{Int64: 1, Valid: false}}
  33.         // 插入数据
  34.         _, err = tx.Exec("INSERT INTO test_table2 (Name, Age) VALUES (?, ?)", x.Name, x.Age)
  35.         if err != nil {
  36.                 tx.Rollback() // 如果出现错误,回滚事务
  37.                 log.Fatal(err)
  38.         }
  39.         // 提交事务
  40.         err = tx.Commit()
  41.         if err != nil {
  42.                 log.Fatal(err)
  43.         }
  44.         // 查询并打印插入后的数据
  45.         fmt.Println("Query results after insertion:")
  46.         queryData(db)
  47.         fmt.Println("查询并打印现有的数据")
  48. }
  49. func queryData(db *sql.DB) {
  50.         rows, err := db.Query("SELECT Name, Age FROM test_table2")
  51.         if err != nil {
  52.                 log.Fatal(err)
  53.         }
  54.         defer rows.Close()
  55.         var nameValue sql.NullString
  56.         var ageValue sql.NullInt64
  57.         for rows.Next() {
  58.                 if err := rows.Scan(&nameValue, &ageValue); err != nil {
  59.                         log.Fatal(err)
  60.                 }
  61.                 fmt.Printf("name: %v, Age: %v\n", nameValue.String, ageValue.Int64)
  62.         }
  63. }
复制代码
效果

这里

给数据库传了0值
改成true后

传值乐成

NameValid改成false

传null
  1. package main
  2. import (
  3.         "database/sql"
  4.         "fmt"
  5.         "reflect"
  6.         "github.com/go-playground/validator/v10"
  7. )
  8. // DbBackedUser 用户结构体,由数据库支持
  9. type DbBackedUser struct {
  10.         Name sql.NullString `validate:"required"` // 可以为NULL的字符串,验证规则为必填
  11.         Age  sql.NullInt64  `validate:"required"` // 可以为NULL的整数,验证规则为必填
  12. }
  13. // 使用单一实例的验证器,它缓存结构体信息
  14. var validate *validator.Validate
  15. func main() {
  16.         // 创建一个新的验证器实例
  17.         validate = validator.New()
  18.         // 注册所有sql.Null*类型,使用自定义的ValidateValuer函数
  19.         validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{})
  20.         // 构建对象进行验证
  21.         // Name字段为空字符串但Valid为true,表示非NULL;Age字段为0且Valid为false,表示NULL
  22.         x := DbBackedUser{Name: sql.NullString{String: "zahngsan", Valid: true}, Age: sql.NullInt64{Int64: 18, Valid: false}}
  23.         // 对x进行结构体验证
  24.         err := validate.Struct(x)
  25.         // 如果验证失败,打印错误信息
  26.         if err != nil {
  27.                 fmt.Printf("Err(s):\n%+v\n", err)
  28.         }
  29. }
  30. // ValidateValuer实现了validator.CustomTypeFunc接口
  31. func ValidateValuer(field reflect.Value) interface{} {
  32.         // 检查field是否为nil
  33.         if !field.IsValid() {
  34.                 return nil
  35.         }
  36.         // 检查field是否实现了sql.Null*类型
  37.         if nullType, ok := field.Interface().(sql.NullString); ok {
  38.                 // 如果Valid为true,返回字符串和非NULL的指示
  39.                 if nullType.Valid {
  40.                         fmt.Println("NullString Valid为true:",nullType.String)
  41.                         return []interface{}{nullType.String, true}
  42.                 }
  43.                 // 如果Valid为false,返回空字符串和NULL的指示
  44.                 fmt.Println("NullString Valid为false")
  45.                 return []interface{}{"", false}
  46.         }
  47.         // 检查field是否实现了sql.NullInt64类型
  48.         if nullInt64, ok := field.Interface().(sql.NullInt64); ok {
  49.                 if nullInt64.Valid {
  50.                         fmt.Println("NullInt64 Valid为true:",nullInt64.Int64)
  51.                         return []interface{}{nullInt64.Int64,true}
  52.                 }
  53.                 fmt.Println("NullInt64 Valid为false")
  54.                 return []interface{}{"", false}
  55.         }
  56.         // 对其他sql.Null*类型进行类似的处理...
  57.         // ...
  58.         // 如果不是sql.Null*类型,返回nil
  59.         return nil
  60. }
复制代码
效果

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表