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

来源:https://www.cnblogs.com/youzhibing/archive/2023/04/28/17353176.html
-Advertisement-
Play Games

開心一刻 中午和哥們一起喝茶 哥們說道:晚上喝酒去啊 我:不去,我女朋友過生日 哥們瞪大眼睛看著我:你有病吧,充氣的過什麼生日 我生氣到:有特麽生產日期的好吧 需求背景 系統對接了外部系統,調用外部系統的介面需要付費,一個介面一次調用付費 0.03 元 同一個月內,同一個介面最高付費 25 元 統計 ...


開心一刻

  中午和哥們一起喝茶

  哥們說道:晚上喝酒去啊

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

  哥們瞪大眼睛看著我:你有病吧,充氣的過什麼生日

  我生氣到:有特麽生產日期的好吧

需求背景

  系統對接了外部系統,調用外部系統的介面需要付費,一個介面一次調用付費 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 )還存在問題,我真的想對官方說一句


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 快速開始 發送請求 >>> import requests >>> r = requests.get('https://api.github.com/events') # GET >>> r = requests.post('https://httpbin.org/post', data={'key ...
  • 原文地址: JavaFx 生成二維碼工具類封裝 - Stars-One的雜貨小窩 之前星之音樂下載器有需要生成二維碼功能,當時用的是一個開源庫來實現的,但是沒過多久,發現那個庫依賴太多,有個http-client的依賴,把軟體都搞大了一倍,而且有時候開發的時候下載依賴還報錯,就想換個方案 於是在網上 ...
  • Golang GMP原理(1) 概念梳理 線程 線程一般指內核級線程,核心如下: 操作系統的最小調度單元 創建 銷毀 調度由內核完成,cpu要完成內核態與用戶態的轉換 可充分利用多核,實現並行 協程 協程線程對應 協程,又稱為用戶級線程,核心點如下: 與線程存在映射關係,為M:1 創建、銷毀、調度在 ...
  • 因所負責的系統使用的spring框架版本5.1.5.RELEASE線上上出過一個偶發的小事故,最後定位為spring-context中的一個bug導致的。 ...
  • 教程簡介 JasperReports入門教程 - 使用包含從環境設置,報告設計,編譯報告設計,填充報告,查看和列印報告,導出,參數,數據源開始的基礎知識到高級知識的初學者教程,簡單易學地設計和創建JasperReports ,欄位,表達式,變數,部分,組,樣式,Scriplets,子報告,圖表,Co ...
  • 來自 https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247532967&idx=1&sn=19790c981aa33502aa1e3a8abe9cd064&chksm=e92a7ca8de5df5befc6cc534cbabdb847e ...
  • 教程簡介 Python 3入門教程 - 從基本概念開始,簡單易學地瞭解Python 3,包括Python 3語法面向對象語言,環境設置,基本語法,變數類型,基本運算符,決策,迴圈,方法,字元串等示例,列表,元組,字典,日期和時間,函數,模塊,文件I / O,工具/實用程式,異常處理,正則表達式,CG ...
  • 大數據時代,各行各業對數據採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現從易到難全方位覆蓋,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為自動化工具 playwright 的使用。 概述 上期文章中講 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...