经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MS SQL Server » 查看文章
当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!
来源:cnblogs  作者:青石路  时间:2023/4/28 9:26:08  对本文有异议

开心一刻

  中午和哥们一起喝茶

  哥们说道:晚上喝酒去啊

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

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

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

需求背景

  系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费 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);
View Code

   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);
View Code

  汇总每个月的付费, 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
View Code

  通用写法, 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 )还存在问题,我真的想对官方说一句

原文链接:https://www.cnblogs.com/youzhibing/p/17353176.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号