jjzjj

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

青石路 2024-02-09 原文

开心一刻

  中午和哥们一起喝茶

  哥们说道:晚上喝酒去啊

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

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

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

需求背景

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

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

  汇总每个月的付费, 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
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 )还存在问题,我真的想对官方说一句

有关当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!的更多相关文章

  1. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  2. ruby-on-rails - ruby on rails 模型验证中的浮点精度 - 2

    我正在尝试使用正则表达式验证美元金额:^[0-9]+\.[0-9]{2}$这工作正常,但每当用户提交表单并且美元金额以0(零)结尾时,ruby(或rails?)将0砍掉。所以500.00变成500.0,因此正则表达式验证失败。有没有办法让ruby​​/rails保持用户输入的格式,而不管尾随零? 最佳答案 我假设您的美元金额是小数类型。因此,用户在字段中输入的任何值在保存到数据库之前都会从字符串转换为适当的类型。验证适用于已转换为数字类型的值,因此在您的情况下,正则表达式并不是真正合适的验证过滤器。不过,您有几种可能性可以解决这个问

  3. ruby-on-rails - Ubuntu 14.04 Rails 丢失文件 - 2

    安装Rails时,一切都很好,但后来,我写道:rails-v和输出:/home/toshiba/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in`require':cannotloadsuchfile--rails/cli(LoadError)from/home/toshiba/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in`r

  4. ruby - 1.8 与 1.9 中的 BigDecimal - 2

    当升级到ruby​​1.9时,我在比较BigDecimal的预期值与实际值时测试失败,这是除float的结果。expected:'0.495E0',9(18)got:'0.49500000000000005E0',18(27)googlingforthingslike"bigdecimalrubyprecision"and"bigdecimalchangesruby1.9"isn'tgettingmeanywhere.HowdidBigDecimal'sbehaviorchangeinruby1.9?update1>RUBY_VERSION=>"1.8.7">1.23.to_d=>#>

  5. ruby - 将 ruby​​ 日期转换为字符串而不丢失格式 - 2

    我正在尝试将RubyDate对象转换为字符串。日期格式为:Sun,15Sep2013但是,当我使用#to_s将其转换为字符串时,它会给出以下内容:"2013-09-15"相反,我希望它变成:"Sun,15Sep2013" 最佳答案 使用Date#strftime有很多选择require'date'date=Date.parse("Sun,15Sep2013")#=>#date.strftime("%a,%d%b%Y")#=>"Sun,15Sep2013" 关于ruby-将ruby​​日期

  6. ruby - Ruby 的任意精度算术 - 2

    Ruby到底是怎么做到的?Jörg或其他人是否知道幕后发生的事情?不幸的是,我不太了解C,所以bignum.c对我帮助不大。我只是有点好奇有人可以解释(用简单的英语)它使用的任何神奇算法背后的理论。irb(main):001:0>999**99936806348825922326789470084006052186583833823203735320465595962143702560930047223153010387361450517521869134525758989639113039318944796977164583238219236607653663113200177617

  7. ruby - 无法将 BigDecimal 强制转换为 BigDecimal - 2

    这应该很简单,但它正在爆炸。有什么想法吗?d=BigDecimal.new("2.0")YAML::load({:a=>d}.to_yaml)TypeError:BigDecimalcan'tbecoercedintoBigDecimalfrom/Users/benjohnson/.rvm/rubies/ruby-1.9.3-p125/lib/ruby/1.9.1/irb/inspector.rb:86:in`inspect'from/Users/benjohnson/.rvm/rubies/ruby-1.9.3-p125/lib/ruby/1.9.1/irb/inspector.rb

  8. ruby-on-rails - Rails 2.3.11 DateTime BigDecimal 精度 - 2

    我目前有一个运行Ruby1.8.7和Rails2.3.2的RubyonRails项目我有一些从数据库中读取数据的单元测试,特别是两个连续项目的日期时间列,这两个项目应该相隔24小时。在一项测试中,我将项目2的日期时间设置为与项目1的日期时间相同。当我执行断言以确保两个值相等时,测试在rails2.3.2下工作正常。当我升级到rails2.3.11时,测试失败显示两次之间的差异将关闭并出现以下错误:expectedbutwas.这两个版本的rails中似乎存在浮点转换问题。如何解决float问题? 最佳答案 这也发生在我身上,我最终这

  9. OSX 上的 Rubygems 丢失 - 2

    我觉得自己像个十足的白痴,但我整天都在研究这个,却一无所获。我重新启动了几次,试图在我的OSX环境、RVM、Brew、Ruby上完全安装Ruby:每次我完成Ruby安装时,它都会在ruby​​gems安装上触底。我不得不重新开始的原因是,在我第一次成功安装(使用打包的一体式RubyInstaller)后,RVM不会在Jewellerybox中更新,所以我无法管理我的gems-这是一个问题通过在/usr/local中而不是在我自己的目录下的系统上的初始Ruby安装。为了解决这个问题,我卸载了我所有的Ruby东西并重新开始。一切正常,直到ruby​​编译器在安装ruby​​2.0.0时开始

  10. 【JAVAEE】JAVA数据库连接(JDBC) - 2

    1.什么是JDBC?Java数据库连接,(JavaDatabaseConnectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是SunMicrosystems的商标。我们通常说的JDBC是面向关系型数据库的。简而言之,JDBC就是JDK提供的关于数据库操作的一套接口规范,不同数据库厂商来负责实现这个接口,完成指定的操作。用程序和数据建立连接,分为三步骤:1.连接数据库2.执行SQL语句3.把查询到的结果集转换成JAVA对象2.对于MySQL的JDBC编程的前期准备工作知识拓展:JAR文件(Java归

随机推荐