GO语言程序查询数据库字段为空遇到的几个问题总结
如果字段值可能为空,那么从表里面读取数据的时候程序使用的变量类型应该使用 sql.NullXXX 类型,比如下面的日期类型:
1 2 3 4 5 | var id uint var createAt time.Time var updateAt time.Time var deleteAt sql.NullTime var name string<br> var gartenId uint<br> var beginAt time.Time<br> var endAt time.Time<br> var monthBegin int<br> var monthEnd int<br> var child []byte<br> var content []byte<br> var creator uint<br> |
开始的时候,deleteAt 使用的也是 time.Time类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 | err = rows.Scan(&id, &createAt, &updateAt, &deleteAt, &name, &gartenId, &beginAt, &endAt, &monthBegin, &monthEnd, &child, &content, &creator) |
这里读取数据都没有问题,但是发现当数据库日期类型字段为NULL的时候,日期类型变量读取到的是0000年的默认日期值,如果稍后再用这个默认值插入数据库,会出现下面的错误:
incorrect datetime value: ‘0000-00-00‘ for column ‘start‘ at row 1
要解决这个问题的办法,就不能使用日期类型变量的默认值插入数据库,可以定义一个引用类型的变量,比如下面的代码,在上面Scan之后将读取出来的变量值赋值给一个结构对象。当然前提得定义变量为sql.NullXXX类型,比如下面代码中的 deleteAt变量:
1 2 3 4 5 6 7 8 9 | var recipe entity.RecipeDO recipe.ID = &id recipe.CreateAt = &createAt recipe.UpdateAt = &updateAt if deleteAt.Valid { recipe.DeleteAt = &deleteAt.Time } recipe.Name = &name |
这样如果数据库字段值为空的话,deleteAt.Valid为假,那么 recipe.DeleteAt 字段就是空了(nil),下面看下 RecipeDO 结构体的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type RecipeDO struct { ID *uint CreateAt *time.Time UpdateAt *time.Time DeleteAt *time.Time Name *string GartenID *uint BeginAt *time.Time EndAt *time.Time MonthBegin *int MonthEnd *int ChildList *[]*ChildForRecipe Content *[]*DailyMenu Creator *uint } |
以后插入数据的时候,判断下结构体字段 DeleteAt是否为空,写不同的插入代码即可,如下示例:
1 2 3 4 5 | if do.DeleteAt != nil { _, err = stmt.Exec(*do.UpdateAt, *do.DeleteAt, *do.Name, *do.GartenID, *do.BeginAt, *do.EndAt, *do.MonthBegin, *do.MonthEnd, jsChildList, jsContent, *do.ID) } else { _, err = stmt.Exec(*do.UpdateAt, nil, *do.Name, *do.GartenID, *do.BeginAt, *do.EndAt, *do.MonthBegin, *do.MonthEnd, jsChildList, jsContent, *do.ID) } |
当然也可以在上面的代码中Exec方法的第二个参数定义一个 sql.NullTime类型,就不用写上面的分支代码了。
PS:
GO语言程序查询数据处理空值的方式还是比较简陋的,容易掉坑里面去。要避免这个问题,最简单的办法还是在建表的时候,给所有字段都设置默认值。当然有时候字段值为NULL有特殊业务含义的话,上面的解决过程是绕不开了。
彩蛋:
上面示例中 RecipeDO 结构体的Content字段是一个复杂结构,数据库对应的表的Content字段是一个json类型,这个字段插入数据库之前必须先Json序列化,补上序列化它们的代码:
1 2 3 4 5 | jsContent,err1 :=json.Marshal(*do.Content) if err1 != nil { logger.Errorf( "Recipe update Content to JSON " , query, err1.Error()) return err1 } |
同样,从数据库读取这个字段,也要反序列化处理一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | err = rows.Scan(&id, &createAt, &updateAt, &deleteAt, &name, &gartenId, &beginAt, &endAt, &monthBegin, &monthEnd, &child, &content, &creator) //其余代码略 var recipe entity.RecipeDO //其余代码略 //反序列化content字段读取的值 var contentObj = make([]*entity.DailyMenu, 0) err = json.Unmarshal(content, &contentObj) if err != nil { logger.Errorf( " GetDO parse content error, " , query, err.Error()) return nil, err } recipe.Content = &contentObj |
参考链接:
go mysql null_Go 查询数据库 Scan Null 字段报错解决办法
https://blog.csdn.net/weixin_30940057/article/details/113566387?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242
go语言scan空值报错
https://blog.csdn.net/leonpengweicn/article/details/51192557
解决Go语言数据库中null值的问题
https://www.jb51.net/article/202690.htm
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构