MySQL 中 FLOAT 和 DOUBLE 屬於浮點數類型,採用 IEEE 754 標準存儲,存在固有的精度缺失問題。直接對 float 列執行 SUM() 聚合,結果往往出現 100.0000000001 這類誤差,導致財務報表、統計分析數據不準確。
-- 修改列類型為精確小數類型
-- DECIMAL(16,6) 表示總共 16 位,小數點後 6 位
ALTER TABLE your_table MODIFY price DECIMAL(16,6);
操作步驟(大表安全變更):
ALTER TABLE t ADD price_new DECIMAL(16,6);UPDATE t SET price_new = price;ALTER TABLE t DROP price, CHANGE price_new price DECIMAL(16,6);優點:從根源解決精度問題,後續所有計算均精確;缺點:存儲空間略大,大表變更需停機或用 pt-online-schema-change。
-- 利用 BINARY 操作符強制二進制上下文計算
SELECT SUM(BINARY price) FROM orders;
BINARY 不是函數,而是類型轉換操作符,它將後續表達式強制視為二進制字符串。在數值運算上下文中,這會改變 MySQL 的內部精度處理邏輯,減少浮點誤差累積。
SELECT SUM(a.number * BINARY b.price) AS total
FROM ler_card AS a
LEFT JOIN ler_items AS b ON a.itemId = b.itemId
WHERE a.user_id = 1;
| 維度 | DECIMAL 改表 | BINARY 查詢轉換 |
|---|---|---|
| 精度保證 | ✅ 完全精確 | ⚠️ 緩解誤差,極端值仍可能偏差 |
| 業務影響 | 需變更表結構,大表需工具在線變更 | 零侵入,僅修改 SQL |
| 性能 | 存儲略大,計算同定點數 | 每次查詢額外轉換開銷 |
| 適用場景 | 財務、金額、庫存等核心業務表 | 歷史表不可改、臨時統計、只讀查詢 |
BINARY 或 CAST(col AS DECIMAL(16,6)) 臨時修正BigDecimal、Python 用 decimal.Decimal、Go 用 shopspring/decimal,避免浮點數流入資料庫不要使用 ROUND(SUM(float_col), 2) 以為解決了問題——這只是掩蓋了誤差,累積誤差超過 0.005 時依然會四捨五入錯誤。