当前位置: 首页 > 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, ?) 

尝试使用 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 类型。

BUT!这种写法存在隐患:

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

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

还有一种写法:

# 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")# 打印
DB Type: VARCHAR, Scan Type: sql.NullString

CAST 直接将字段按映射成了 string 类型,当其值为 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不够通用

http://www.xdnf.cn/news/779923.html

相关文章:

  • 学习路之PHP--easyswoole使用视图和模板
  • MFC Resource.h 文件详解与修改指南
  • nginx+tomcat动静分离、负载均衡
  • JavaScript性能优化实战:从核心原理到工程实践的全流程解析
  • 【大模型:知识图谱】--1.py2neo连接图数据库neo4j
  • Neo4j 数据建模:原理、技术与实践指南
  • Java详解LeetCode 热题 100(25):LeetCode 141. 环形链表(Linked List Cycle)详解
  • JVM—垃圾收集算法和HotSpot算法实现细节
  • Kerberos面试内容整理-Kerberos 的配置与排障
  • 力扣每日一题——分发糖果
  • React Native图片预加载:让你的应用图片预览像德芙一样丝滑
  • 实验设计与分析(第6版,Montgomery著,傅珏生译) 第10章拟合回归模型10.9节思考题10.1 R语言解题
  • Python趣学篇:从零打造智能AI井字棋游戏(Python + Tkinter + Minimax算法)
  • 编译 Linux openssl
  • 黑客利用GitHub现成工具通过DevOps API发起加密货币挖矿攻击
  • C++语法系列之类型转换
  • Catboost算法原理及应用场景
  • 生成对抗网络(GAN)基础原理深度解析:从直观理解到形式化表达
  • C语言学习—数据类型20250603
  • NLP学习路线图(二十):FastText
  • K8S上使用helm部署 Prometheus + Grafana
  • Grafana-State timeline状态时间线
  • 乐播视频v4.0.0纯净版体验:高清流畅的视听盛宴
  • Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(六):图片上传功能
  • Linux(线程概念)
  • 《深入解析SPI协议及其FPGA高效实现》-- 第三篇:FPGA实现关键技术与优化
  • Docker 安装 Centos
  • Python与数据分析期末复习笔记
  • Web3如何重塑数据隐私的未来
  • LeetCode 139. 单词拆分(Word Break) - 动态规划深度解析