当前位置: 首页 > news >正文

【mysql】BIGINT UNSIGNED字段被表示为float科学计数法 丢失精度问题

1. 问题

我有如下 sql 查询:

rows, err := db.Query("SELECT COALESCE(creator, ?) FROM table1 LIMIT 1;", "")
if err != nil {return err
}
defer rows.Close()for rows.Next() {var dest stringif err = rows.Scan(&dest); err != nil {return err}fmt.Println("dest: ", dest)  // 值为 6.743046165522305e+16 ?
}

发现数据库存储的 creator 内容是 67430461655223049但是查询结果却被变成了一个科学计数法的 string: 6.743046165522305e+16 

creator 的类型明明是一个 BIGINT UNSIGNED 无符号整型:

`creator` BIGINT UNSIGNED NOT NULL DEFAULT 0

为什么会被扫描成一个 float 格式的字符串的呢?

2. sql 分析

增加一段打印,查看 go-sql-driver 驱动解析出的类型:

colTypes, _ := rows.ColumnTypes()
for _, ct := range colTypes {fmt.Printf("DB Type: %s, Scan Type: %v\n", ct.DatabaseTypeName(), ct.ScanType())
}

发现这个字段真的被驱动解析成了 float 类型:

DB Type: DOUBLE, Scan Type: float64

(1) 使用原本字段

尝试修改 sql 语句:

# sql
SELECT creator FROM table1 LIMIT 1;# 打印
DB Type: UNSIGNED BIGINT, Scan Type: uint64

原始 creator 类型是 uint 没错,那问题就出在了 COALESCE 上

(2) 使用 CAST(COALESCE(creator, ?) AS UNSIGNED)

尝试使用 CAST 函数,将类型手动转换为 uint:

# sql
SELECT CAST(COALESCE(creator, ?) AS UNSIGNED) FROM table1 LIMIT 1;# 打印
DB Type: UNSIGNED BIGINT, Scan Type: uint64# 查询结果
67430461655223048

类型对了,但是发现读出的数据不对了!

应该是 67430461655223049,查询出来变成了 67430461655223048,牙白!丢精度了!

因为:驱动想用 float64 去接收的,但我强制将它转成了 uint,在某些环境下可能会丢失精度。

float64 无法精确表示所有 uint64 范围内的整数,特别是超过 2^53 的数字,就会发生舍入误差。

(3) 使用 COALESCE(CAST(creator AS CHAR), ?)

也就是说,它想用 float,我不能强制转为 uint,那我可以强制转为 string 啊!

# sql
SELECT COALESCE(CAST(creator AS CHAR), ?) FROM table1 LIMIT 1;# 打印
DB Type: VARCHAR, Scan Type: sql.NullString# 查询结果
67430461655223049

ok 结果正确

(4) 使用 COALESCE(creator, '')

在尝试不同的 sql 语句过程中我发现,当我使用 COALESCE(creator, ?) 并传入空字符串作为参数,和直接写死 COALESCE(creator, ‘’) 的行为是不同的,会直接影响字段类型的解析结果:

# sql
SELECT COALESCE(creator, ?) FROM ..# 被解析为float
DB Type: DOUBLE, Scan Type: float64# sql
SELECT COALESCE(creator, '') FROM ..# 被解析为string
DB Type: VARCHAR, Scan Type: string

这是由于: MySQL 在执行查询时会为每个字段生成元信息(metadata),包括字段名、字段类型、是否可能为 NULL、是否是数字类型等。当使用 COALESCE(creator, ?) 并传参 "" 时,驱动不知道 ? 的实际类型是什么,它会尝试根据 creator(BIGINT UNSIGNED)和参数的默认类型进行推断。go-sql-driver 就会把参数当作 float64 来处理(因为它是数值型),整个 COALESCE(...) 表达式被推断为 DOUBLE 类型。

也就是说,使用这种写法 COALESCE(creator, '') 也可以达到我们的目标。

BUT!这种写法存在隐患:

  • 类型推导不稳定:MySQL可能在某些版本/配置下将其推导为DOUBLE,导致科学计数法(6.743046165522305e+16)
  • 依赖驱动行为:不同版本的go-sql-driver/mysql可能解析出不同Go类型(string/float64)

(5) 使用  IFNULL(CAST(creator AS CHAR), ?)

还有一种写法,用 IFNULL 函数:

# sql
SELECT IFNULL(CAST(creator AS CHAR), ?) FROM table1 LIMIT 1;# 打印
DB Type: VARCHAR, Scan Type: sql.NullString# 查询结果
67430461655223049

也是可以的。

但是 IFNULL 不是标准 SQL,仅限于 MySQL 环境下使用。如果你希望代码兼容其他数据库(如 PostgreSQL、达梦),应该优先使用 COALESCE。

3. 解决

最佳写法:

rows, err := db.Query("SELECT COALESCE(CAST(creator AS CHAR), ?) FROM table1 LIMIT 1;", "", "89448245134135417")
  • CAST(creator AS CHAR):将字段 creator 强制转换为字符串类型(CHAR),驱动会用 sql.NullString 类型来接收
  • COALESCE(..., ?):如果第一个参数不为 NULL,就返回;否则返回第二个参数(即空字符串)

4. 几种 sql 写法对比

方案
类型安全
精度保证
推荐
SELECT creator
✅uint64
❌不能处理NULL值
COALESCE( creator , ?)
❌ 变为float64
❌可能出现科学计数法表示
COALESCE(creator, '')
❌ 可能string也可能变为float64
❌依赖驱动版本
CAST(COALESCE(creator, ?) AS UNSIGNED)
✅uint64
❌ 会将float转为uint丢失精度
❌丢失精度
COALESCE(CAST(creator AS CHAR), ?)
✅sql.NullString
IFNULL(CAST(creator AS CHAR), ?)
✅sql.NullString
不是标准SQL不够通用

相关文章:

  • C++初赛的三讲
  • Java详解LeetCode 热题 100(25):LeetCode 141. 环形链表(Linked List Cycle)详解
  • web第八次课后作业--分层解耦
  • PS教程-萌新系统入门课课程视频+素材
  • String 学习总结
  • 力扣刷题 -- 232. 用栈实现队列
  • Android系统进程优先级
  • 组相对策略优化(GRPO):原理及源码解析
  • UE5 2D角色PaperZD插件动画状态机学习笔记
  • 支持TypeScript并打包为ESM/CommonJS/UMD三种格式的脚手架项目
  • 【python】三元图绘制(详细注释)
  • javascript 实战案例 二级联动下拉选框
  • 杭州白塔岭画室怎么样?和燕壹画室哪个好?
  • 6.RV1126-OPENCV 形态学基础膨胀及腐蚀
  • Spring Boot整合Druid与Dynamic-Datasource多数据源配置:从错误到完美解决
  • 1. 引言
  • SQL注入漏洞-上篇
  • Qwen2.5-VL 视觉编码器的SwiGLU
  • 车载软件架构 --- 软件定义汽车开发模式思考
  • 一、类模板
  • ctcms做的比较好的网站/持啊传媒企业推广
  • 朝阳做网站的公司/长沙百度搜索排名
  • 网站的logo在百度怎么显示不出来/网站优化排名金苹果下拉
  • 属于b2b电子商务网站/南京seo推广
  • 网络销售网站外包/百度信息流怎么做效果好
  • 成都网站建设全平台/百度公司注册地址在哪里