记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑
开心一刻
刚毕业的侄子给我发消息
侄子:叔,人生太难了
我:怎么呢?
侄子:工作太难了,感情也太难了,怎么什么都这么难
我:你还小啊
侄子:大了就不难了?
我:大了你就习惯了

问题复现
先准备表:数据源( tbl_datasource
)以及数据
DROP TABLE IF EXISTS `tbl_datasource`;
CREATE TABLE `tbl_datasource` (
`id` bigint(20) NOT NULL COMMENT 'ID',
`name` varchar(100) NOT NULL COMMENT '数据库名称',
`type` varchar(50) NOT NULL COMMENT '数据库类型',
`data_supplier_id` varchar(32) NULL DEFAULT NULL COMMENT '数据商id',
`data_supplier_name` varchar(200) NULL DEFAULT NULL COMMENT '数据商名称',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `database_un_idx1`(`name`) USING BTREE
) COMMENT = '数据源';
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (250521675081322496, 'MYSQL250521675081322496', 'MYSQL', '1909915075751776256', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (250523399661686784, 'MYSQL250523399661686784', 'MYSQL', '1909915075751776256', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (250894313117061120, 'HIVE250894313117061120', 'HIVE', '1909915075751776256', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (1912678810919714817, 'FTP1912678810919714817', 'FTP', '1909915075751776300', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (1912794318679678977, 'KAFKA1912794318679678977', 'KAFKA', '1909915075751776300', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (1913070130303217665, 'FTP1913070130303217665', 'FTP', '1909915075751776300', '供应商1');
INSERT INTO `tbl_datasource`(`id`, `name`, `type`, `data_supplier_id`, `data_supplier_name`) VALUES (1913070213291716609, 'FTP1913070213291716609', 'FTP', '1909915075751776256', '供应商1');
有个字段我要特别说明下,data_supplier_id
对应数据商表(tbl_data_supplier
)的 id
CREATE TABLE `tbl_data_supplier` (
`id` bigint(20) NOT NULL,
`supplier_name` varchar(50) DEFAULT NULL,
`supplier_code` varchar(20) DEFAULT NULL,
`state` int NOT NULL DEFAULT '1' COMMENT '状态(0-无效、1-有效)',
`alias` varchar(20) DEFAULT NULL,
`type` tinyint DEFAULT '1' COMMENT '数据商类型(1采购数据商、2意向数据商)',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) COMMENT='数据商表';
大家看仔细了,它俩类型不一致
data_supplier_id varchar(32) → id bigint(20)
铺垫已经完成,接下来我们看一个查询
SELECT * FROM tbl_datasource WHERE data_supplier_id = 1909915075751776256;
你们觉得会查出几条记录,是不是也觉得是 4 条?可实际上呢?

查出来 7 条,把 data_supplier_id = '1909915075751776256'
的 3 条记录也查出来了,这是为什么?

问题排查
如果是急着解决问题,我猜你们会这么处理
SELECT * FROM tbl_datasource WHERE data_supplier_id = '1909915075751776256';
查询结果如下

以字符串值去查询,不仅仅是你们的下意识,也是我的下意识,为什么会有这样的下意识,出于两点考虑
从
WHERE data_supplier_id = 1909915075751776256
的查询结果上来看,1909915075751776256
与1909915075751776300
只有最后 3 位不一致,是不是正好精度丢失忽略了最后 3 位,所以这两个数值比较是相等的?对于大数值,JavaScript 有精度丢失问题
字符串的比较是不存在精度一说的
data_supplier_id
类型不是varchar(32)
吗,为什么要用整形值去查?
虽然问题能够快速解决,但用整形值去查,查询结果不对的原因还没找到,真的是精度丢失问题?需要我们去验证,如何验证了,可以去查官方文档,但我们要缩小查询范围,有针对性的去查;有 1 点是可以肯定的
data_supplier_id 是 varchar 类型的,用整型值去查,肯定存在类型转换
既然存在类型转换,那就看看 MySQL 对类型转换的说明:Type Conversion in Expression Evaluation
注意开头这段话
When an operator is used with operands of different types, type conversion occurs to make the operands compatible. Some conversions occur implicitly.
翻译过来就是
当运算符与不同类型的操作数一起使用时,会进行类型转换以使操作数兼容。有些转换是隐式发生的
很显然,我们案例中的转换就是隐式发生的,也就是我们平时说的 隐式转换
;我们继续往下看,重点看转换规则

简单翻译一下
如果一个或两个参数是
NULL
,比较结果是NULL
关于
NULL
,推荐大家看看:神奇的 SQL 之温柔的陷阱 → 为什么是 IS NULL 而非 = NULL ?如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较
如果两个参数都是整数,则将它们作为整数进行比较
如果不与数字进行比较,十六进制值将被视为二进制字符串
时间类型的说明
如果其中一个参数是十进制值,则比较取决于另一个参数。如果另一个参数是十进制或整数值,则将参数作为十进制值进行比较,如果另一参数是浮点值,则将其作为浮点值进行比较
在所有其他情况下,参数都作为浮点(双精度)数字进行比较。例如,字符串和数字操作数的比较是作为浮点数的比较进行的
我们的案例是不是正好适用于最后那条规则?
字符串和数字操作数的比较是作为浮点数的比较进行的
继续往下看官方文档,关于浮点数有如下说明

浮点数与大整数值的比较,得到的是一个近似结果(而非一个精确结果),因为在比较之前,整数被转换为双精度浮点,这无法准确表示所有64位整数。有些整数值无法用浮点数精确表示,所以就存在四舍五入导致精度丢失,至于是入还是舍,跟平台(CPU、计算机体系结构、编译器版本等)有关系。
官方还给出了案例说明
mysql> SELECT '9223372036854775807' = 9223372036854775807;
-> 1
mysql> SELECT '9223372036854775807' = 9223372036854775806;
-> 1
类比我们的案例

字面值不相等的两个值,比较结果竟然是相等的,这就是因为隐式转换成浮点数比较,比较结果是一个近似值(而非精确值),而这个近似值可能是不准确的!问题的原因是不是就找到了?

问题解决
问题找到了,解决起来就简单了,方式有以下几种
用字符串值查,而非整数值查
SELECT * FROM tbl_datasource WHERE data_supplier_id = '1909915075751776256';
调整
data_supplier_id
类型成bigint
,与表tbl_data_supplier
的id
类型保持一致,然后用整数值查SELECT * FROM tbl_datasource WHERE data_supplier_id = 1909915075751776256;
推荐这种方式,更规范,查询效率也更高
显示将整数值转成字符串
利用
CAST
、CANCAT
等函数,将整数值转成字符串SELECT * FROM tbl_datasource WHERE data_supplier_id = CAST(1909915075751776256 AS CHAR);
SELECT * FROM tbl_datasource WHERE data_supplier_id = CONCAT(1909915075751776256);
不要使用,相比于1、2,啥也不是
总结
类型转换可能会导致索引失效,还可能会导致精度丢失,一定要避免
不管是建表,还是查询,要规范起来,否则就隐藏着各种坑
表
tbl_data_supplier
的id
字段是bigint
类型,为什么tbl_datasource
的data_supplier_id
字段要用varchar(32)
类型;既然data_supplier_id
是varchar(32)
类型了,为什么代码中的查询又用整数值(WHERE data_supplier_id = 1909915075751776256
)?后面我问了下 deepseek,感觉比搜索引擎搜的更精准,比盲翻官方文档更快速,推荐大家使用
记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑的更多相关文章
- ORACLE绑定变量隐式转换导致性能问题
年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多 另外就是发现出现了较多的等待事件,主要有latch: ...
- 隐式转换导致的cpu负载近100%
1.背景:从昨天晚上通过钉钉和邮箱一直接收到频繁报cpu负载超过90%,刚好BI同事晚上.凌晨在线上配合审计频繁DML数据库(备注:BI有一个同事有个库的DML权限,后面等审计完会收回)加上我线上线下 ...
- mysql字符串的隐式转换导致查询异常
如果mysql某个字段(name)类型为varchar, 加了索引,在执行where查询的时候,传入了int的值,这样就会全表扫描,把每一条的值都转换成int(会出现"中国"-&g ...
- MySQL SQL优化之字符串索引隐式转换
之前有用户很不解:SQL语句非常简单,就是select * from test_1 where user_id=1 这种类型,而且user_id上已经建立索引了,怎么还是查询很慢? test_1的表结 ...
- SQL Server有意思的数据类型隐式转换问题
写这篇文章的时候,还真不知道如何取名,也不知道这个该如何将其归类.这个是同事遇到的一个案例,案例比较复杂,这里抽丝剥茧,仅仅构造一个简单的案例来展现一下这个问题.我们先构造测试数据,如下所示: CRE ...
- 数栈SQL优化案例:隐式转换
MySQL是当下最流行的关系型数据库之一,互联网高速发展的今天,MySQL数据库在电商.金融等诸多行业的生产系统中被广泛使用. 在实际的开发运维过程中,想必大家也常常会碰到慢SQL的困扰.一条性能不好 ...
- SQL Server中提前找到隐式转换提升性能的办法
http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...
- SQL Server 隐式转换引发的躺枪死锁-程序员需知
在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...
- 【转】SQL SERVER标量表达式的隐式转换
在SQL Server中的数据类型中,存在着优先级的问题.标量表达示的返回结果类型也会根据操作数的类型而定,如1 +'1'=2.而不是'11',因些Int型的优先级比VARCHAR型的优先级要高.所以 ...
- SQL Server 隐式转换引发的死锁
在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...
随机推荐
- rbd常用的配置参数
本文分享自天翼云开发者社区<rbd常用的配置参数>,作者:l****n rbd的基本介绍 rbd的架构如下图所示: rbd采用CRUSH算法实现数据的随机分布.CRUSH算法,即Contr ...
- Air实现Go程序的热重载(热加载)
简介: air是Go的热加载工具,它可以监听文件或者目录的变化,自动编译,重启程序,提高开发的工作效率. 场景: 在代码修改后需要通过ctrl+c来停止项目,go run的方式来再次重启项目,在开发进 ...
- FFT & NTT & FWT
只是学习笔记,真心推荐 cmd ,他讲的真的细到把所有的前置知识都讲了一遍. \[FBI \ WARNING:本篇 NTT 部分非常不完善 \] FFT & NTT & FWT 大杂烩 ...
- 微信企业付款到零钱(Java版)
订阅专栏1.开通条件. 商户注册超过90天且,连续30天有交易,可以每天支付1元来刷,目前测试可行.随后在微信商户平台 - 产品管理自动开启,然后需要申请. 疑问: 话说是要有公众号appid才行,但 ...
- 让Typecho支持Emoji表情,解决报错:Database Query Error
最近在使用一个主题时,看到搭配emoji表情可以让改主题更加美观,于是我就上了,结果在将emoji表情放进去保存的时候报错:Database Query Error,于是问起了度娘.最后的结果是: 在 ...
- 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
引子:那个让运维集体加班的夜晚 "凡哥!线上服务响应时间飙到10秒了!"凌晨1点,实习生小李的语音带着哭腔. 监控大屏上,JVM堆内存曲线像坐了火箭--刚扩容的16G内存,30分钟 ...
- 【由技及道】镜像圣殿建造指南:Harbor私有仓库的量子封装艺术【人工智障AI2077的开发日志009】
摘要:当容器镜像需要同时存在于8个平行宇宙时,就像在量子计算机里管理72个维度的镜像分身.本文记录一个未来AI如何通过Harbor搭建量子镜像圣殿,让容器分发成为跨越时空的瞬间传送. 动机:镜像管理的 ...
- excel怎么根据数值做进度条
开始->条件格式->数据条
- 【P0】Logisim部件级实验/有限状态机
课上 过得十分狼狈.经鉴定孩子可能脑子拗 T1 投票决议 组内投票,赞成>反对,则通过:组长拥有一票否决权. 信号名 方向 描述 [1:0] s Input 2'b00 赞成2'b01 反对2' ...
- Ollama——大语言模型本地部署的极速利器
1.概述 Ollama 是一款开源跨平台大模型工具,主要用于在本地便捷部署和运行大型语言模型(LLM),核心目标是降低用户使用大模型的门槛,同时保障数据隐私.核心功能与特点如下: (1)本地部署,隐私 ...