開心一刻
中午和哥們一起喝茶
哥們說道:晚上喝酒去啊
我:不去,我女朋友過生日
哥們瞪大眼睛看著我:你有病吧,充氣的過什麼生日
我生氣到:有特麼生產日期的好吧
需求背景
系統對接了外部系統,呼叫外部系統的介面需要付費,一個介面一次呼叫付費 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 )還存在問題,我真的想對官方說一句