应避免用round(price, 2)分组,因浮点二进制近似导致语义相同值被拆分;正确做法是用case区间分组或floor(price*100)/100.0截断,且聚合前须cast每行至decimal再累加。

因为浮点数在底层用 IEEE 754 双精度表示,0.1 + 0.2 实际存为 0.30000000000000004,SUM 是对这些近似值累加,误差随行数增长而固化——不是 SUM() 出错,是输入数据本身就不精确。
GROUP BY 时用 ROUND(price, 2) 分组为什么反而更乱?
ROUND 是逐行计算的,而两个原始值(比如 19.995 和 19.994999999999998)在二进制中本就不同,ROUND 后可能一个得 20.00、另一个得 19.99,人为把语义相同的值拆成两组。
- 别写
GROUP BY ROUND(price, 2)—— 这不是“按两位小数分组”,是强制归并,业务逻辑易错 - 想按价格区间聚合,用
CASE WHEN price BETWEEN 0 AND 99.99 THEN '0-99.99' END更可靠 - 若必须数值分段,
FLOOR(price * 100) / 100.0(截断)比ROUND()稳定,避开 0.5 边界抖动
为什么 SUM(CAST(x AS DECIMAL(10,2))) 还不准?
关键在执行顺序:如果原始列是 FLOAT,先 SUM() 再 CAST,误差已累积进总和;正确做法是每行先转定点数,再累加。
- 错误写法:
SUM(CAST(SUM(price) AS DECIMAL(10,2)))—— 外层 CAST 没用,内层 SUM 已失准 - 正确写法:
SUM(CAST(price AS DECIMAL(10,2)))—— 强制每行转 DECIMAL 后再求和 - DECIMAL 参数要留余量:
DECIMAL(20,2)比DECIMAL(10,2)更安全,防 SUM 后整数位溢出 - MySQL 非严格模式下,CAST 溢出会静默截断,务必查
SELECT @@sql_mode
WHERE 条件里写 price = 19.99 为什么总不命中?
因为存进去的就不是精确的 19.99,而是类似 19.990000000000002 或 19.989999999999998 的近似值,直接等值匹配几乎必失败。
- 安全写法一(推荐):
WHERE CAST(price AS DECIMAL(10,2)) = 19.99 - 安全写法二(容忍误差):
WHERE ABS(price - 19.99) - 别写
WHERE ROUND(price, 2) = 19.99—— 先查清你数据库里ROUND()返回类型,MySQL 返回DOUBLE,PostgreSQL 返回NUMERIC,行为不一致 - ORM 层若把 DECIMAL 字段当成字符串或 double 绑定,照样触发隐式转换丢精度
真正容易被忽略的是:一旦类型链断裂(比如某个中间值被当成 DOUBLE),后续所有计算都跟着漂移,而数据库不报错、不警告——它只是默默给你一个“看起来对”的错值。










