当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!
开心一刻
中午和哥们一起喝茶
哥们说道:晚上喝酒去啊
我:不去,我女朋友过生日
哥们瞪大眼睛看着我:你有病吧,充气的过什么生日
我生气到:有特么生产日期的好吧

需求背景
系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费 0.03 元
同一个月内,同一个接口最高付费 25 元
统计每个月的付费情况
需求清楚了不?不清楚? 给大家举个案例

这下明白了吧
明白了需求,相信大家都会觉得很简单,不就是一个分组汇总吗?
客官说的对,但生活总会给我们一点 surprise

我们慢慢往下看
环境准备
SQL Server 版本: SQL Server 2017
MySQL 版本: 8.0.27
引入 MySQL ,是为了跟 SQL Server 做对比
SQL Server 建表并初始化数据

CREATE TABLE tbl_interface_call_times (
id BIGINT PRIMARY KEY IDENTITY(1,1),
call_month INT NOT NULL,
interface varchar(50) NOT NULL ,
times INT NOT NULL
);
INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
(202301, 'interface1', 800),
(202301, 'interface2', 1000),
(202301, 'interface3', 100),
(202302, 'interface1', 833),
(202302, 'interface2', 834),
(202302, 'interface3', 134),
(202302, 'interface4', 243),
(202302, 'interface5', 2143);
MySQL 建表并初始化数据

CREATE TABLE tbl_interface_call_times (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
call_month INT NOT NULL COMMENT '月份',
interface varchar(50) NOT NULL COMMENT '接口',
times INT NOT NULL COMMENT '调用次数',
PRIMARY KEY(id)
) COMMENT '接口调用次数';
INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
(202301, 'interface1', 800),
(202301, 'interface2', 1000),
(202301, 'interface3', 100),
(202302, 'interface1', 833),
(202302, 'interface2', 834),
(202302, 'interface3', 134),
(202302, 'interface4', 243),
(202302, 'interface5', 2143);
汇总每个月的付费, SQL 该如何写?
很简单的啦,如下所示

SELECT call_month,
SUM(
CASE WHEN times * 0.03 > 25 THEN 25
ELSE times * 0.03
END
) monthFee
FROM tbl_interface_call_times
GROUP BY call_month

通用写法, SQL Server 和 MySQL 都支持
我们看下查询结果

一切都很正常,觉得世界真美好!

问题复现
我们不能光玩数据库吧?

不得像这样雨露均沾?
必须把 spring-boot 、 MyBatis-Plus 安排上
mysql-jdbc 版本: 8.0.21 , mssql-jdbc 版本: 6.2.1.jre8
完整代码:mybatis-plus-dynamic-datasource
访问: http://localhost:8081/interface/summary?startMonth=202301&endMonth=202302
你会发现,你心心念念的 surprise 终于出现了!

正确应该是 86.3,.3 哪去了?

直查数据库是没问题的呀

莫非 MyBatis-Plus 有问题?
我们切到 MySQL 试试;将 InterfaceCallTimesServiceImpl 上的数据源改成 mysql_db

然后重启,我们再访问: http://localhost:8081/interface/summary?startMonth=202301&endMonth=202302

这说明应该不是 MyBatis 的问题,那不完犊子了?

问题解决
是不是束手无策了? 也不是,我们可以 Bing 一下的嘛
你会发现说的都是批量 insert 的时候, BigDecimal 有精度丢失
单条插入的时候,是没有精度丢失的
然后了,大家试出了一条件论: 批量插入数据时,如果插入的数据精度不统一,最终入库的数据精度统一按最低的精度入库
虽说我们只是查询,莫非也需要 精度统一 ?
精度统一
试试呗,反正又不要钱

重启,神奇的事情发生了

.3 它回来了! 相信此刻的你肯定有一种与知己久别重逢的激动

问题貌似解决了,但说实话,这种处理方式你用的放心吗?
升级 mssql-jdbc 版本
我们好好捋一下,程序从 SQL Server 获取数据,经历了哪些环节?
只有三个: MyBatis-Plus -> mssql-jdbc -> SQL Server
前面我们已经排除了 SQL Server 和 MyBatis-Plus
那问题肯定就出在 mssql-jdbc 身上了
问题又来了,该如何从 mssql-jdbc 上找问题了?
开源的东西从它的官方找相关的 issue ,肯定不止我们遇到这样的问题,那么肯定有人会给官方提了 issue
issue 地址: https://github.com/microsoft/mssql-jdbc/issues
直接搜索 BigDecimal ,像这样

回车之后,你会发现,原来你不是一个人在战斗

那就去里面找呗,发现 #1489 跟我们的问题有点像,仔细去读,发现关联了 #1912

读到 1912 的末尾,你会发现又关联了 #2051,我们去看看 2051

那就是在这里修复了呀,那它关联的版本是哪个了?

然后我们在回到我们搜索 BigDecimal 相关 issue 的时候,你会发现

12.2.0 已经发布了

如果觉得看英文的费劲,那就看中文的:Microsoft JDBC Driver for SQL Server 发行说明

这总看得懂了吧
那就将 mssql-jdbc 升级到 12.2.0 试试

入参不用统一精度,结果也正确了!
但是,又开始转折了,你以为 12.2.0 就高枕无忧了?

BigDecimal 的问题都延续到 12.3.0 了
此刻大家的心情是怎样的,请评论区说明
总结
1、当 mssql-jdbc 遇上 BigDecimal ,两种处理方式
1.1 BigDecimal 类型的入参全部统一成最高精度
1.2 版本升级到 12.2.0 ,但还是有问题,需要考虑业务是否会触发 12.2.0 的 bug
2、 mssql-jdbc 的 BigDecimal 的问题从 2016 年就开始出现了,到了现在( 2023 )还存在问题,我真的想对官方说一句

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!的更多相关文章
- sql System.Data.SqlClient.SqlError: 无法覆盖文件 'C:\Program Files\Microsoft SQL Server\MSSQL\data\itsm_Data.MDF'。数据库 'my1' 正在使用该文件的解决方案
对数据库备份进行还原时遇到“sql System.Data.SqlClient.SqlError: 无法覆盖文件 'C:\Program Files\Microsoft SQL Server\MSSQ ...
- SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)
本文从零开始一步一步介绍如何在Ubuntu上搭建SQL Server 2017,包括安装系统.安装SQL等相关步骤和方法(仅供测试学习之用,基础篇). 一. 创建Ubuntu系统(Create U ...
- Configure Always On Availability Group for SQL Server on Ubuntu——Ubuntu上配置SQL Server Always On Availability Group
下面简单介绍一下如何在Ubuntu上一步一步创建一个SQL Server AG(Always On Availability Group),以及配置过程中遇到的坑的填充方法. 目前在Linux上可以搭 ...
- SQL Server 无法在服务器上访问指定的路径或文件解决方法
SQL Server 无法在服务器上访问指定的路径或文件解决方法 在SQL Server附加数据库或备份数据库时出现:无法在服务器上访问指定的路径或文件. 请确保您具有必需的安全权限且该路径或文件存在 ...
- 安装 sql server 2008出现重启电脑,另在server 2012 r2安装sql server 2008 安装不上
时即使是进行电脑重启,也会报这个错误,那么就不是电脑的问题了,其实是系统注册表在作怪,解决方法如下: 1.开始-->运行,输入regedit,打开注册表管理器: 2. 找到 HKEY_LOCAL ...
- 手工注入——sql server (mssql)注入实战和分析
前言 首先要对sql server进行初步的了解.常用的全部变量@@version:返回当前的Sql server安装的版本.处理器体系结构.生成日期和操作系统.@@servername:放回运行Sq ...
- sql server 局域网与公网上的发布与订阅
一台局域网的服务器,可以访问公网. 一台云端的服务器. 要求:将局域网中的服务器部分数据库同步到云端的服务器上. 配置情况: win server 2012 是发布服务器. win server 20 ...
- eclipse使用jdbc方式连接sql server 2012数据库史上最新最详细教程(2015年4月已亲测)
步骤分为3部:1.通过sql server 配置管理器配置1433端口 2.将sqljdbc41.jar类库添加到对应的工程中 3.在java程序中连接数据库 步骤1:打开sql server ...
- sql server 小记——分区表(上)
我们知道很多事情都存在一个分治的思想,同样的道理我们也可以用到数据表上,当一个表很大很大的时候,我们就会想到将表拆 分成很多小表,查询的时候就到各个小表去查,最后进行汇总返回给调用方来加速我们的查询速 ...
- SQL Server选取本周或上一周数据
有关SQL Server中有关周的数据查询主要思路来自下面这个语句 select getdate(), dateadd(wk, datediff(wk, 0, DateAdd(Day,-1,getda ...
随机推荐
- hi,docker,docker的介绍
一.docker的介绍 1.什么是docker: docker是一种虚拟化技术,小型的系统环境(linux)2.虚拟化技术: 在计算机中用例管理虚拟资源的一种手段 内存管理.软件虚拟化.硬件虚拟化(磁 ...
- 6-SSRF漏洞
1.SSRF漏洞介绍 SSRF是一种由攻击者构造请求,由服务端发起请求的安全漏洞.一般情况下,ssrf攻击的目标是外网无法访问的内部系统. 2.SSRF原理 Ssrf的形成大多是由于服务端提供了从其他 ...
- axios post params(post像get一样使用params传参)
今天有一个接口是/create?name=***&age=****,但是是一个post请求,我只见过get的这种请求. 找了好久- 解决方法: 发表的axios签名是axios.post(ur ...
- Must be called at the top of a `setup` function vue3使用vue-i18n时出现的报错
在某js文件中引入 import {useI18n} from "vue-i18n"; 使用:useI18n().t('APP_LOADING') 修改后: import i18n ...
- avx
编译选项: CXXFLAGS += -mavx2 #include <immintrin.h> #include <cmath> void reset_xy(int *x, i ...
- 微信网页授权——获取code、access_token、openid,及跨域问题解决
首先在微信开发文档中有提到微信网页授权的操作步骤: 第一步:用户同意授权,获取code 在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中 ...
- git提交的时候,报错yarn run v1.21.1 ,SyntaxError: Cannot use import statement outside a module 解决
原因是 lint-staged这个依赖中,需要的node的版本是, 而我使用的node版本是12.13.1 ,切换成14.17.0就可以了
- 在Vim编辑器中查找选定文本
按照任意选定文本查找: 1. 进入visiual模式选定文本, 按 y 键复制: 2. 按 / 键进入查找模式: 3. 按 Ctrl + r 打开vim寄存器: 4. 按 " 键将寄存器内容 ...
- MySQL如何指定字符集和排序规则?
在MySQL中,可以使用以下两种方式指定字符集和排序规则: 创建数据库或表时指定字符集和排序规则 在创建数据库或表时,可以使用 CHARACTER SET 和 COLLATE 选项指定字符集和排序规则 ...
- vsftpd2.3.4 后门笑脸漏洞
漏洞概要 在vsftpd 2.3.4版本中,在登录输入用户名时输入:)类似于笑脸的符号,会导致服务处理开启6200后门端口,直接执行系统命令 漏洞利用 攻击机:kali 2022 msfconsole ...