【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 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
qetfw1 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
数智园区合作社2 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai
lbb 小魔仙2 小时前
Windows搭建PaddleOCR+飞书Webhook自动打卡通知机器人:识别、推送与远程触发完整教程
数据库·人工智能·prometheus·时序数据库
captain3763 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
心中有你02144 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
龙亘川4 小时前
综合素质评价业务建模:从过程性评价到成长档案全链路闭环
数据库·智慧城市·数据可视化
数据库小组4 小时前
NineData 实战教程:MySQL 5.5 数据迁移同步到 MySQL 8.0(跨版本升级)
数据库·mysql·mysql优化·数据库开发·数据库迁移·版本升级·数据复制
zhangjw345 小时前
1-5 PostgreSQL 数据类型全集:基础类型、字符串、数值、时间类型
数据库·postgresql
王中阳Go5 小时前
5.27 分钟干完后端和全部测试:一场三小时笔试的五个失分点,四个不在代码上
大数据·数据库·人工智能·后端·大语言模型