开心一刻

  中午和哥们一起喝茶

  哥们说道:晚上喝酒去啊

  我:不去,我女朋友过生日

  哥们瞪大眼睛看着我:你有病吧,充气的过什么生日

  我生气到:有特么生产日期的好吧

需求背景

  系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费 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 → 精度丢失,真坑!的更多相关文章

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

  2. SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)

    本文从零开始一步一步介绍如何在Ubuntu上搭建SQL Server 2017,包括安装系统.安装SQL等相关步骤和方法(仅供测试学习之用,基础篇). 一.   创建Ubuntu系统(Create U ...

  3. 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上可以搭 ...

  4. SQL Server 无法在服务器上访问指定的路径或文件解决方法

    SQL Server 无法在服务器上访问指定的路径或文件解决方法 在SQL Server附加数据库或备份数据库时出现:无法在服务器上访问指定的路径或文件. 请确保您具有必需的安全权限且该路径或文件存在 ...

  5. 安装 sql server 2008出现重启电脑,另在server 2012 r2安装sql server 2008 安装不上

    时即使是进行电脑重启,也会报这个错误,那么就不是电脑的问题了,其实是系统注册表在作怪,解决方法如下: 1.开始-->运行,输入regedit,打开注册表管理器: 2. 找到 HKEY_LOCAL ...

  6. 手工注入——sql server (mssql)注入实战和分析

    前言 首先要对sql server进行初步的了解.常用的全部变量@@version:返回当前的Sql server安装的版本.处理器体系结构.生成日期和操作系统.@@servername:放回运行Sq ...

  7. sql server 局域网与公网上的发布与订阅

    一台局域网的服务器,可以访问公网. 一台云端的服务器. 要求:将局域网中的服务器部分数据库同步到云端的服务器上. 配置情况: win server 2012 是发布服务器. win server 20 ...

  8. eclipse使用jdbc方式连接sql server 2012数据库史上最新最详细教程(2015年4月已亲测)

    步骤分为3部:1.通过sql server 配置管理器配置1433端口   2.将sqljdbc41.jar类库添加到对应的工程中   3.在java程序中连接数据库 步骤1:打开sql server ...

  9. sql server 小记——分区表(上)

    我们知道很多事情都存在一个分治的思想,同样的道理我们也可以用到数据表上,当一个表很大很大的时候,我们就会想到将表拆 分成很多小表,查询的时候就到各个小表去查,最后进行汇总返回给调用方来加速我们的查询速 ...

  10. SQL Server选取本周或上一周数据

    有关SQL Server中有关周的数据查询主要思路来自下面这个语句 select getdate(), dateadd(wk, datediff(wk, 0, DateAdd(Day,-1,getda ...

随机推荐

  1. Java学习笔记(二)环境

     卸载JDK 1.删除java的安装目录 2.删除JAVA_HOME 3.删除path下关于java的目录 4.java -version 配置环境变量 1.我的电脑-->右键-->属性 ...

  2. bash transpose csv

    transpose() { awk ' BEGIN { FS = ","; OFS = ","; } { if (max_nf<NF) max_nf=NF ...

  3. Net6读取AppSettings.json

    1.创建Helper类 public class AppHelper { private static IConfiguration _config; public AppHelper(IConfig ...

  4. qemu4.0+libvirt5.6.0编译使用

    1. qemu4.0.0 apt install pythonapt install libpixman-1-dev./configure --prefix=/usrmakemake install ...

  5. [转]常见的视频编码详解 Cinepak Codec by Radius

    AVI所采用的压缩算法并无统一的标准.也就是说,同样是以AVI为后缀的视频文件,其采用的压缩算法可能不同,需要相应的解压软件才能识别和回放该AVI文件.除了Microsoft公司之外,其他公司也推出了 ...

  6. Vue前端框架的基础学习,为之后打个基础

    Vue 首先,在使用vue之前,我们需要先下载好一个vue.js文件,才能继续进行vue的学习,下载教程如下: 1.进入官网:https://v2.vuejs.org/ 2.选择页面中的Learn,再 ...

  7. 自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序

    学习操作系统原理最好的方法是自己写一个简单的操作系统. 前面铺垫了这么久,今天终于开始写程序了.本讲将介绍3个逐步深入但非常简单的程序,一方面是让大家熟悉开发流程,另一方面是顺便解决前面遇到的CPU占 ...

  8. Oracle数据库 insert 插入数据 显示问号乱码的解决办法

    一.问题描述 插入的中文数据 显示成问号(乱码),其他语言如老挝文.柬文等都一样. 二.解决方案 plsql插入oracle数据乱码问题处理起来其实很简单,因为乱码问题一般都是由于编码不一致导致的,我 ...

  9. 分布式事务组件Seata

    ‌介绍 一阶段:事务协调者通知每一个服务处理本地事务,每个服务开始处理但是不会提交事务,处理完毕后告知协调者. 二阶段:协调者收到所有服务的消息后通知他们提交事务. ‌重要角色 事务管理器(TM),决 ...

  10. Object 通用方法

    Object 通用方法 概览 public final native Class<?> getClass() public native int hashCode() public boo ...