开心一刻

刚毕业的侄子给我发消息
侄子:叔,人生太难了
我:怎么呢?
侄子:工作太难了,感情也太难了,怎么什么都这么难
我:你还小啊
侄子:大了就不难了?
我:大了你就习惯了

问题复现

先准备表:数据源( 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';

查询结果如下

以字符串值去查询,不仅仅是你们的下意识,也是我的下意识,为什么会有这样的下意识,出于两点考虑

  1. WHERE data_supplier_id = 1909915075751776256 的查询结果上来看,19099150757517762561909915075751776300 只有最后 3 位不一致,是不是正好精度丢失忽略了最后 3 位,所以这两个数值比较是相等的?

    对于大数值,JavaScript 有精度丢失问题

    字符串的比较是不存在精度一说的

  2. 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

类比我们的案例

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

问题解决

问题找到了,解决起来就简单了,方式有以下几种

  1. 用字符串值查,而非整数值查

    SELECT * FROM tbl_datasource WHERE data_supplier_id = '1909915075751776256';
  2. 调整 data_supplier_id 类型成 bigint,与表 tbl_data_supplierid 类型保持一致,然后用整数值查

    SELECT * FROM tbl_datasource WHERE data_supplier_id = 1909915075751776256;

    推荐这种方式,更规范,查询效率也更高

  3. 显示将整数值转成字符串

    利用 CASTCANCAT 等函数,将整数值转成字符串

    SELECT * FROM tbl_datasource WHERE data_supplier_id = CAST(1909915075751776256 AS CHAR);
    SELECT * FROM tbl_datasource WHERE data_supplier_id = CONCAT(1909915075751776256);

    不要使用,相比于1、2,啥也不是

总结

  1. 类型转换可能会导致索引失效,还可能会导致精度丢失,一定要避免

  2. 不管是建表,还是查询,要规范起来,否则就隐藏着各种坑

    tbl_data_supplierid 字段是 bigint 类型,为什么 tbl_datasourcedata_supplier_id 字段要用 varchar(32) 类型;既然 data_supplier_idvarchar(32) 类型了,为什么代码中的查询又用整数值(WHERE data_supplier_id = 1909915075751776256 )?

  3. 后面我问了下 deepseek,感觉比搜索引擎搜的更精准,比盲翻官方文档更快速,推荐大家使用

记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑的更多相关文章

  1. ORACLE绑定变量隐式转换导致性能问题

    年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多 另外就是发现出现了较多的等待事件,主要有latch: ...

  2. 隐式转换导致的cpu负载近100%

    1.背景:从昨天晚上通过钉钉和邮箱一直接收到频繁报cpu负载超过90%,刚好BI同事晚上.凌晨在线上配合审计频繁DML数据库(备注:BI有一个同事有个库的DML权限,后面等审计完会收回)加上我线上线下 ...

  3. mysql字符串的隐式转换导致查询异常

    如果mysql某个字段(name)类型为varchar, 加了索引,在执行where查询的时候,传入了int的值,这样就会全表扫描,把每一条的值都转换成int(会出现"中国"-&g ...

  4. MySQL SQL优化之字符串索引隐式转换

    之前有用户很不解:SQL语句非常简单,就是select * from test_1 where user_id=1 这种类型,而且user_id上已经建立索引了,怎么还是查询很慢? test_1的表结 ...

  5. SQL Server有意思的数据类型隐式转换问题

    写这篇文章的时候,还真不知道如何取名,也不知道这个该如何将其归类.这个是同事遇到的一个案例,案例比较复杂,这里抽丝剥茧,仅仅构造一个简单的案例来展现一下这个问题.我们先构造测试数据,如下所示: CRE ...

  6. 数栈SQL优化案例:隐式转换

    MySQL是当下最流行的关系型数据库之一,互联网高速发展的今天,MySQL数据库在电商.金融等诸多行业的生产系统中被广泛使用. 在实际的开发运维过程中,想必大家也常常会碰到慢SQL的困扰.一条性能不好 ...

  7. SQL Server中提前找到隐式转换提升性能的办法

        http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...

  8. SQL Server 隐式转换引发的躺枪死锁-程序员需知

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...

  9. 【转】SQL SERVER标量表达式的隐式转换

    在SQL Server中的数据类型中,存在着优先级的问题.标量表达示的返回结果类型也会根据操作数的类型而定,如1 +'1'=2.而不是'11',因些Int型的优先级比VARCHAR型的优先级要高.所以 ...

  10. SQL Server 隐式转换引发的死锁

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...

随机推荐

  1. useradd usermod userdel passwd groupadd groupmod groupdel等命令详解

    linux命令参数记忆有些模糊了,记录下.学而时习之,不亦说乎? 1.useradd命令用来建立用户帐号和创建用户的起始目录,使用权限是终极用户.创建新用户useradd,默认的用户家目录会被存放在/ ...

  2. CTFHub-RCE漏洞wp

    引言 题目共有如下类型 什么是RCE漏洞 RCE漏洞,全称是Remote Code Execution漏洞,翻译成中文就是远程代码执行漏洞.顾名思义,这是一种安全漏洞,允许攻击者在受害者的系统上远程执 ...

  3. 闲话 717 - LGV 引理的小应用

    这是我们的某一天的联考题目: \(n\le 500\). 显然使用平面图完美匹配计数可以获得 \(O(n^6)\),但是有一种神秘的对路径的双射.当时我们都认为这是超级人类智慧,但是今天看书发现是书上 ...

  4. mybatis之xml简单映射,解决实体类属性字段与数据库表字段不一致问题

    当实体类属性字段与数据库表字段不一致时该怎么办? 方法一:起别名 <select id="getUserList" resultType="RealUser&quo ...

  5. 科研界DeepSeek+AI应用协作攻略来了!

    自从DeepSeek爆火 AI应用届开启"精英集结" 与DeepSeek携手撑起国产AI一片天 比如,DeepSeek+Midjourney 成为设计师的好帮手 DeepSeek+ ...

  6. nginx: [error] open() “/usr/local/var/run/nginx.pid” failed (2: No such file or directory)

  7. Hive - [01] 概述

    一.Hive是什么 是Facebook开源,用于解决海量结构化日志的数据统计工具. 是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能. Hive处理的数 ...

  8. CentOS 8 上安装和配置 nginx

    1.检查yum上的nginx版本 yum info nginx 2.安装nginx yum install nginx 安装过程有时会询问是否安装,输入y回车即可 3.将服务设置为每次开机启动 sud ...

  9. Elasticsearch搜索引擎学习笔记(五)

    搜索功能 数据准备 1.自定义词库 慕课网 慕课 课网 慕 课 网 2.新建立索引shop 3.建立mappings POST /shop/_mapping (7.x之前的版本:/shop/_mapp ...

  10. Selenium WebDriver上创建 WebDriver测试脚本

    本文实现一个WebDriver测试脚本,介绍WebDrive的常用命令.UI元素定位的策略以及在脚本中的使用,还有Get命令. 你将学到:  脚本创建  代码走查  测试执行  定位Web元素 ...