在MySQL中保留三位小数,通常出现在金额、比率、物理量采集等场景。如果小数位过多,不仅浪费存储,还会让业务展示显得不精确;如果小数位过少,又可能丢失有效精度。实际处理时可以从表结构定义和查询函数两个层面来解决,二者适用阶段不同,效果也有差异。

一、建表时通过DECIMAL类型固定三位小数
最推荐的做法是在设计表结构时,使用DECIMAL(M, 3)来声明字段。其中M代表总位数(含整数和小数),3代表小数点后固定保留三位。这样数据在写入时就会按三位小数存储,不会出现浮点误差。
例如我们要记录商品的价格,可以定义如下结构。DECIMAL在MySQL内部以字符串形式保存精确值,不像FLOAT或DOUBLE存在二进制近似问题,因此特别适合财务类数据。
CREATE TABLE product (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
price DECIMAL(10, 3) NOT NULL COMMENT '商品价格,保留三位小数'
);
INSERT INTO product(name, price) VALUES('键盘', 12.3456);
-- 实际存入的是 12.346(四舍五入)
SELECT name, price FROM product;
上面的例子中,插入的12.3456被自动四舍五入为12.346。如果我们插入12.3454,则会存为12.345。这种在存储层约束的方式,能保证所有后续读取都不必再处理小数位问题。
需要注意的是,DECIMAL的M值要估算好。若业务整数位可能很大,比如千万级金额,那么DECIMAL(10,3)就只能存最大9999999.999,超出会报错或截断,应提前按业务量级设计。
二、查询时使用ROUND函数保留三位小数
如果表字段已经是FLOAT、DOUBLE或者整数,不想改表结构,可以在查询时用ROUND()函数。ROUND(col, 3)会对字段值进行四舍五入,返回保留三位小数的结果。
ROUND属于计算函数,不改变原表数据,只在结果集里生效,适合报表统计、接口输出等只读场景。下面示例对一个DOUBLE字段做处理:
SELECT id, amount, ROUND(amount, 3) AS amount_3 FROM order_log;
假设amount原值是123.45678,ROUND(amount,3)得到123.457;若原值是123.45432,则得到123.454。这种写法灵活,但每次查询都有计算开销,且如果原字段是FLOAT,本身不准,ROUND后也只是“看起来”三位小数。
另外,ROUND对负数的处理也符合数学四舍五入规则,例如ROUND(-2.3456, 3)得到-2.346,不会偏离预期。
三、使用TRUNCATE函数直接截断
有些业务不允许四舍五入,例如按比例抽成时多给一分都违规,此时用TRUNCATE(col, 3)直接截断到三位小数,后续位无论大小都丢弃。
TRUNCATE和ROUND最大的区别就是不做进位。下面用同一组数据对比二者差异:
SELECT val, ROUND(val, 3) AS rnd, TRUNCATE(val, 3) AS trc FROM ( SELECT 1.23456 AS val UNION ALL SELECT 9.99999 ) t;
上述查询中,1.23456经ROUND得1.235,经TRUNCATE得1.234;9.99999经ROUND得10.000,经TRUNCATE得9.999。可以看出在边界值上二者结果可能差很多,选型时要看业务是否允许进位。
TRUNCATE同样只影响输出,不修改底层数据。如果字段是DECIMAL但scale不是3,用TRUNCATE也能快速统一展示格式。
四、格式化展示用FORMAT函数
如果目的仅是给人看,比如后台报表,可以用FORMAT(col, 3)。它返回带千分位逗号、固定三位小数的字符串,方便阅读,但不适合再参与数值计算。
FORMAT会隐式将结果转为字符串,且四舍五入规则同ROUND。示例如下:
SELECT FORMAT(1234567.89123, 3) AS fmt; -- 输出:1,234,567.891
因为返回的是字符串,如果在程序里还需要加减,要先转回DECIMAL或DOUBLE,否则拼接会变成文本运算。因此FORMAT只建议放在最外层展示使用。
五、不同方案对比与选型
为方便理解,我们把四种常见方式放在一张表里比较:
| 方式 | 作用层 | 是否四舍五入 | 返回类型 | 适用场景 |
|---|---|---|---|---|
| DECIMAL(M,3) | 存储 | 是(写入时) | 数值 | 金额等精准业务 |
| ROUND(col,3) | 查询 | 是 | 数值 | 统计输出 |
| TRUNCATE(col,3) | 查询 | 否 | 数值 | 禁止进位场景 |
| FORMAT(col,3) | 查询 | 是 | 字符串 | 报表展示 |
从维护成本看,存储层用DECIMAL最省心,后期所有读取天然合规;查询函数适合遗留表改造。若系统同时有展示与计算需求,推荐DECIMAL存原始精度,查询时按场景用ROUND或TRUNCATE包裹。
最后提醒,不要对FLOAT字段直接ROUND后还以为精准,浮点本身的误差可能在更低位累积。重要数据务必从源头用DECIMAL,才能真正确保三位小数既好看又可信。