惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 青石路

Claude Code安装,接入阿里云百炼模型,蹭蹭免费额度 异源数据同步 → 记一次 DataX 已同步数据量优化 明明连接的是Redis的DB0,为什么能查到DB3的数据? TINYINT(1) 类型的字段,明明数据存的是 2,为什么查出来是 true 用了MySQL的INSERT ON DUPLICATE KEY UPDATE,怎么还报唯一索引冲突错误 那些年不该放到事务中的操作,你实现过哪些 关于布尔类型的变量不要加 is 前缀,被网友们吐槽了,特来完善下 都说了布尔类型的变量不要加 is 前缀,非要加,这不是坑我了嘛 安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题排查与处理 经由同个文件多次压缩的文件MD5都不一样问题排查,感慨AI的强大! 记一次cannot access its superinterface问题的的排查 → 强如Spring也一样写Bug SpringBoot支持Kafka多源配置的同时还要支持启停配置化,是真的会玩 如果XXL-JOB执行器在执行某任务中被重启了,重启后该任务能够被自动弥补调度吗 Spring Boot读取外部配置文件失败,原因绝对出乎你意料 不依赖 Spring,你会如何自实现 RabbitMQ 消息的消费(一) 异源数据同步 → DataX 同步启动后如何手动终止? 异源数据同步 → 如何获取 DataX 已同步数据量? 记一次 RabbitMQ 消费者莫名消失问题的排查 不升级 POI 版本,如何生成符合新版标准的Excel 2007文件 以MySQL为例,来看看maven-shade-plugin如何解决多版本驱动共存的问题? maven 插件之 maven-shade-plugin,解决同包同名 class 共存问题的神器
记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑
青石路 · 2025-04-27 · via 博客园 - 青石路

开心一刻

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

开心一刻

问题复现

先准备表:数据源( 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 = '1909915075751776300' 的 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.

翻译过来就是

当运算符与不同类型的操作数一起使用时,会进行类型转换以使操作数兼容。有些转换是隐式发生的

很显然,我们案例中的转换就是隐式发生的,也就是我们平时说的 隐式转换;我们继续往下看,重点看转换规则

mysql类型转换规则

简单翻译一下

  • 如果一个或两个参数是 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,感觉比搜索引擎搜的更精准,比盲翻官方文档更快速,推荐大家使用

    deepseek回答