开心一刻

  中午和哥们一起喝茶

  哥们说道:晚上喝酒去啊

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

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

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

需求背景

  系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费 0.03 元

  同一个月内,同一个接口最高付费 25 元

  统计每个月的付费情况

  需求清楚了不?不清楚? 给大家举个案例

  这下明白了吧

  明白了需求,相信大家都会觉得很简单,不就是一个分组汇总吗?

  客官说的对,但生活总会给我们一点 surprise

  我们慢慢往下看

环境准备

   SQL Server 版本: SQL Server 2017

   MySQL 版本: 8.0.27

  引入 MySQL ,是为了跟 SQL Server 做对比

   SQL Server 建表并初始化数据

  1. CREATE TABLE tbl_interface_call_times (
  2. id BIGINT PRIMARY KEY IDENTITY(1,1),
  3. call_month INT NOT NULL,
  4. interface varchar(50) NOT NULL ,
  5. times INT NOT NULL
  6. );
  7. INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
  8. (202301, 'interface1', 800),
  9. (202301, 'interface2', 1000),
  10. (202301, 'interface3', 100),
  11. (202302, 'interface1', 833),
  12. (202302, 'interface2', 834),
  13. (202302, 'interface3', 134),
  14. (202302, 'interface4', 243),
  15. (202302, 'interface5', 2143);

   MySQL 建表并初始化数据

  1. CREATE TABLE tbl_interface_call_times (
  2. id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  3. call_month INT NOT NULL COMMENT '月份',
  4. interface varchar(50) NOT NULL COMMENT '接口',
  5. times INT NOT NULL COMMENT '调用次数',
  6. PRIMARY KEY(id)
  7. ) COMMENT '接口调用次数';
  8. INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
  9. (202301, 'interface1', 800),
  10. (202301, 'interface2', 1000),
  11. (202301, 'interface3', 100),
  12. (202302, 'interface1', 833),
  13. (202302, 'interface2', 834),
  14. (202302, 'interface3', 134),
  15. (202302, 'interface4', 243),
  16. (202302, 'interface5', 2143);

  汇总每个月的付费, SQL 该如何写?

  很简单的啦,如下所示

  1. SELECT call_month,
  2. SUM(
  3. CASE WHEN times * 0.03 > 25 THEN 25
  4. ELSE times * 0.03
  5. END
  6. ) monthFee
  7. FROM tbl_interface_call_times
  8. 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. 磊磊零基础打卡算法:day18 c++模拟哈希表来模拟散列表

    5.21 哈希表 Hash表又称为散列表,一般由Hash函数(散列函数)与链表结构共同实现,与离散化思想类似. 一般要求:防止冲突,便于查询 模拟hash表: 拉链法:两个核心操作insert(),f ...

  2. Matlab %补充---用的多的函数

    Input  promat = 'This is a sentence.' x = input(prompt) %显示prompt中的文本并等待用户输入数值或者表达式后按Return %如果用户什么都 ...

  3. JavaScript 函数的方法

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. Jmeter之post上传文件(jmeter接口测试请求参数上传文件)

    一,上传excel等普通文件 接口测试时有接口文档的话,那就对着文档写,没api文档,就自己抓包看了. 接口文档 抓包查看 步骤一:接口请求切换至文件上传(Files Upload)栏 content ...

  5. PHP 二维按照某个字段对数组排序

    function arraySort($arr, $keys, $type = 'asc') {//二维按照某个字段对数组排序 $keysvalue = $new_array = array(); f ...

  6. BeanFactory与FactoryBean区别

    1. BeanFactory BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂.在Spring中,BeanFactory是IOC容器的核心接口,也是 ...

  7. 关于office 16

    word是office的组件之一,Excel也是其中之一. 一用有八大组件.  

  8. For循环用法-打印乘法表

      for循环可以遍历某一对象(遍历:通俗点说,就是把这个循环中的第一个元素到最后一个元素依次访问一次).for循环的结构如下 具体例子打印乘法表: #打印乘法表: for i in range(1, ...

  9. 【Beat】Scrum Meeting 1

    时间:2021年6月26日 1.各个成员今日完成的任务以及贡献小时数 姓名 今日完成任务 贡献小时数 鑫 编写软件的功能测试方案文档,录制视频演示软件系统安装配置过程 4 荣娟 编写软件的功能测试方案 ...

  10. laravel groupBy 分页

    $model=DB::table('tablebname') ->where(function($query) use ($res){ $query->where('xx','xx'); ...