
DECIMAL 是唯一能保证金额计算不出现 0.1 + 0.2 = 0.30000000000000004 这类错误的数据类型。 FLOAT 和 DOUBLE 在底层用二进制近似表示十进制小数,从存入那一刻起就可能失真;而 DECIMAL 把数字当字符串一样逐位存,运算也按十进制规则执行,结果可预测、可审计。
为什么 FLOAT 存 0.1 就会出错?
因为 0.1 的十进制小数无法用有限位二进制精确表达——就像 1/3 = 0.333... 在十进制里无限循环一样。FLOAT 把它截断成近似值(如 0.10000000149011612),后续所有加减乘除都在这个误差基础上累加。你看到的 SELECT 结果看似正常,只是 MySQL 做了四舍五入显示,实际值已经漂移。
常见错误现象:
- 插入
INSERT INTO t VALUES(0.1), (0.2),再查SUM(col) = 0.3返回0 - WHERE 条件用
amount = 19.99查不到刚插入的记录 - 导出到 Excel 后金额末尾多出一串 9 或 1(比如
19.990000000000002)
DECIMAL(M,D) 中 M 和 D 怎么定才安全?
M 是总位数(含小数点前和后),D 是小数位数。选错会导致截断或溢出,不是“越大越好”。
电商价格典型配置是 DECIMAL(10,2),但要注意:
- 整数部分最多 8 位 → 最大支持
99999999.99,超了会报Out of range value for column 'amount' - 如果涉及分润、汇率换算等中间计算,
D=2不够用:乘除后小数位会膨胀,建议按业务保留至少 4 位(如DECIMAL(12,4)),最后再 round 到两位展示 -
DECIMAL(10,0)是默认值,没写 D 就等于没有小数位,千万别漏写
用 BIGINT 存“分”真的比 DECIMAL 更快更省?
理论上,整数运算确实比定点数快,存储也固定(8 字节)。但代价是业务逻辑全得自己处理单位转换,容易出错。
真实踩坑点:
- 前端传
19.99元,后端忘记 ×100 直接插进BIGINT字段 → 存成 19 元 - 优惠券满 300 减 50,判断逻辑写成
if (amount >= 300),但 amount 是“分”,实际要写if (amount >= 30000) - 报表 SQL 里漏了
/100.0,金额全显示为整数元,财务直接报警 - MySQL 8.0+ 对
DECIMAL做了大量优化,性能差距已远不如十年前明显
真正该警惕的,是那些没显式声明 D(小数位)的 DECIMAL 字段,或者把 FLOAT 当 DECIMAL 用还加 ROUND() 掩盖问题——精度一旦丢失,后面所有计算都不可信。











