【Mysql】字段隐式转换对where条件和join关联条件的影响

【Mysql】字段隐式转换对where条件和join关联条件的影响

【一】问题描述

数据库中用户表主键id是bigint类型,由雪花算法生成的id。另一个中间表的字段userId存的是varchar类型。使用MybatisPlus执行sql,传参是Long类型,出现以下两种情况:

(1)使用Long类型的参数userId=728666272023183375,从中间表里删除记录,会把userId=728666272023183374的记录也删除了

(2)使用用户表和中间表leftjoin的使用,用户表的id是bigint,中间表的userId是varchar,728666272023183375的用户会把728666272023183374的关联信息也查出来

根据上述情况分析,无论是where查询还是关联表join,都存在等号两边的字段类型不一致,出现字段类型隐式转换导致的精度丢失,导致查询错误

【二】问题分析

上述问题的核心原因是数据库隐式类型转换导致的数值匹配异常,具体如下:

【1】类型不匹配触发隐式转换(字符转数值)

用户表的id是bigint类型(数值型),中间表的userId是varchar类型(字符串型)。当用Long类型(对应数据库bigint)的参数与varchar字段比较时,数据库会触发隐式类型转换:将varchar类型的userId转换为数值型(而非将bigint转换为字符串)后再进行比较。

【2】大整数转换的精度丢失问题

MyBatis-Plus 雪花算法默认生成的 ID 是 19 位长整数,雪花算法生成的 ID 是 18-19 位的大整数(如728666272023183375)

MySQL 中用于存储大整数的主要类型是BIGINT,其精度存在硬性限制:

(1)BIGINT是 64 位有符号整数,取值范围为 -9223372036854775808 ~ 9223372036854775807(约 ±9e18)。

(2)这个范围对应的最大有效数字是18 位(9223372036854775807 是 19 位数字,但前 18 位是有效范围)。

数据库对数值类型的精度处理存在限制:

部分数据库(如 MySQL)对bigint的最大支持是 64 位有符号整数(范围约 ±9e18),但当varchar字段存储的字符串转换为数值时,若数值接近最大范围,可能因精度不足导致近似匹配(如728666272023183375和728666272023183374在转换后被误判为相等)。

字符串转数值时,若存在格式问题(如末尾有不可见字符),也可能导致转换后的数值与预期不符,进而匹配错误。

【3】具体场景解释

删除场景:执行DELETE FROM 中间表 WHERE userId = ?时,参数是Long类型(数值),数据库将userId(varchar)转为数值后比较。因精度丢失,728666272023183375和728666272023183374的转换结果被误判为相等,导致误删。

关联查询场景:LEFT JOIN的ON条件中,user.id(bigint)与中间表.userId(varchar)比较时,同样触发隐式转换,导致相近 ID 的关联信息被错误匹配。

【三】字段类型转换的原理

数据库处理不同类型字段的比较时,遵循隐式类型转换规则,核心逻辑如下:

【1】转换方向:字符串→数值(而非数值→字符串)

当数值类型(bigint/int)与字符串类型(varchar)比较时,数据库默认将字符串转换为数值后再比较,而非将数值转为字符串。原因是数值比较的优先级高于字符串比较(数据库认为数值语义更精确)。

【2】转换规则与风险

若字符串是纯数字(如"123"),转换为数值后可正常比较。

若字符串包含非数字字符(如"123a"),转换后可能为0或NULL(不同数据库行为不同)。

若字符串是超长数字(如 19 位以上),转换时可能因超出数据库数值类型的精度范围,导致截断、四舍五入或溢出,进而出现匹配错误(即本文问题的根源)。

【3】转换过程的细节:字符串→数值的溢出处理

MySQL 将字符串转换为数值时,遵循以下规则(以 19 位纯数字字符串为例):

(1)尝试将字符串解析为BIGINT类型(默认优先匹配最大的整数类型)。

(2)若字符串数值≤9223372036854775807(BIGINT最大值):可精确转换(18 位及以内的数值通常没问题)。

(3)若字符串数值>9223372036854775807:

MySQL 无法用BIGINT精确表示,会自动转为DOUBLE(双精度浮点数)处理。

但DOUBLE的精度有限(有效数字为 15-17 位),对于 19 位数值,超出 17 位的部分会被四舍五入或截断,导致精度丢失。

【4】示例:

字符型字段存储:"9223372036854775808"(19 位,超过BIGINT最大值)。

转换为DOUBLE后,可能被近似为9223372036854776000(后几位因精度不足被截断)。

此时若查询条件的数值参数是9223372036854775808,转换后与字段转换结果可能匹配,也可能因近似值冲突导致错误匹配(如与9223372036854775809的转换结果相同)。

【5】典型现象:相近数值的错误匹配

由于 19 位数值转换后精度丢失,会导致原本不同的数值在转换后被判定为相等,表现为:

用WHERE user_id = 728666272023183375(数值参数)查询时,可能同时匹配"728666272023183375"和"728666272023183374"(字符型字段值)。

原因是这两个 19 位数值转换为DOUBLE后,因精度限制被近似为同一个值(后几位差异被忽略)。

【四】解决方法

核心原则:避免隐式类型转换,确保比较双方类型一致。具体实现如下:

【1】方案1:传参时将Long转为String

在 Java 代码中,将Long类型的userId转换为String后再传入 SQL,确保参数与varchar字段类型一致,避免触发隐式转换。

java 复制代码
// 错误:传Long类型
Long userId = 728666272023183375L;
queryWrapper.eq("user_id", userId); // 触发隐式转换

// 正确:转为String后传参
String userIdStr = String.valueOf(userId);
queryWrapper.eq("user_id", userIdStr); // 字符串直接比较,无转换

【2】方案2:统一表字段类型

最彻底的方式是修改中间表的userId字段类型为bigint,与用户表的id类型保持一致,从根源上消除类型转换问题。

相关推荐
瓯雅爱分享1 天前
Java+Vue构建的采购招投标一体化管理系统,集成招标计划、投标审核、在线竞价、中标公示及合同跟踪功能,附完整源码,助力企业实现采购全流程自动化与规范化
java·mysql·vue·软件工程·源代码管理
BTU_YC1 天前
Neo4j查询计划完全指南:读懂数据库的“执行蓝图“
数据库·neo4j
非极限码农1 天前
Neo4j图数据库上手指南
大数据·数据库·数据分析·neo4j
mit6.8241 天前
[C# starter-kit] 命令/查询职责分离CQRS | MediatR |
java·数据库·c#
咋吃都不胖lyh1 天前
SQL-多对多关系
android·mysql·数据分析
苏打水com1 天前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
shan~~1 天前
linux达梦数据库操作
linux·数据库·chrome
武文斌771 天前
项目学习总结:LVGL图形参数动态变化、开发板的GDB调试、sqlite3移植、MQTT协议、心跳包
linux·开发语言·网络·arm开发·数据库·嵌入式硬件·学习
CoderIsArt1 天前
SQLite架构
数据库·sqlite
lixora1 天前
银河麒麟高级服务器操作系统(ADM64 版)V10(SP1)搭建 Oracle 19c RAC
数据库