开发财务、充值、消费、余额相关业务时,经常遇到金额展示需求:数据库原始小数位数较多,前端需要统一保留两位小数展示。
很多同学习惯把原始数值查询出来,在 Python / Java 代码里做四舍五入处理。
实际上 MySQL 原生提供数值函数,可以直接在SQL层面完成四舍五入。
本文重点讲解 ROUND() 函数,区分容易混淆的 TRUNCATE()、FORMAT(),同时梳理财务场景踩坑点、NULL空值兼容方案,给出现成可直接复制的业务模板。
|
1 |
ROUND(数值, 保留小数位数) |
|
1 2 3 |
SELECT ROUND(123.456, 2); -- 123.46 SELECT ROUND(123.454, 2); -- 123.45 SELECT ROUND(100, 2); -- 100.00 |
统计充值、余额、消费金额场景:
|
1 2 3 4 |
-- 当日充值总额,求和后四舍五入保留2位 SELECT ROUND(SUM(money), 2) AS day_recharge FROM sys_recharge_log WHERE create_time >= CURDATE() AND create_time < CURDATE() + INTERVAL 1 DAY; |
一个高频坑:当查询没有匹配数据时,SUM() 结果为 NULL,ROUND(NULL,2) 依旧返回NULL。
程序如果直接使用会出现空值报错。
解决方案:IFNULL() 兜底,不存在数据时返回 0.00。
标准生产写法:
|
1 2 3 |
SELECT IFNULL(ROUND(SUM(money), 2), 0.00) AS day_recharge FROM sys_recharge_log WHERE create_time >= CURDATE() AND create_time < CURDATE() + INTERVAL 1 DAY; |
逻辑说明:
|
1 |
SELECT TRUNCATE(123.459, 2); -- 123.45 |
适用场景:只想要舍弃后面小数、不进位;财务金额统计严禁使用,会造成金额误差。
|
1 |
SELECT FORMAT(IFNULL(ROUND(SUM(money),2),0), 2); |
特性:
总结区分:
? 金额统计、后续还要运算:ROUND + IFNULL
? 财务计算不要用 TRUNCATE
? 需要二次运算不要使用 FORMAT
仪表盘统计多条金额指标,一次性统一处理小数:
|
1 2 3 4 5 6 |
SELECT IFNULL(ROUND(SUM(CASE WHEN type=1 THEN money ELSE 0 END),2),0.00) AS money1, IFNULL(ROUND(SUM(CASE WHEN type=2 THEN money ELSE 0 END),2),0.00) AS money2, IFNULL(ROUND(SUM(price),2),0.00) AS consume FROM sys_balance WHERE expire_time >= NOW() AND status = 1 AND user_id = 'xxx'; |
优点:
缺点:
复杂计算逻辑全部压在数据库,极端大量并发场景需要评估压力。
优点:灵活,可以针对不同场景切换保留位数;
缺点:
建议规范:单纯展示类报表、后台统计页面,优先SQL层统一ROUND。
? 错误写法
|
1 |
ROUND(IFNULL(SUM(money),0.00), 2) |
逻辑没问题,但推荐下面可读性更强顺序;
? 推荐顺序
|
1 |
IFNULL(ROUND(SUM(money), 2), 0.00) |
没有业务数据时 SUM 返回 NULL,前端展示出现空白。务必搭配 IFNULL。
|
1 2 3 |
# 危险!FORMAT返回带逗号的字符串,转float报错 row['day_recharge'] = "1,200.50" float(row['day_recharge']) # 转换异常 |
浮点类型本身存在精度丢失,最佳实践:金额使用 DECIMAL(18,2) 存储,从根源避免精度问题。
|
1 2 3 4 |
SELECT IFNULL(ROUND(SUM(money), 2), 0.00) AS num FROM sys_recharge_log WHERE create_time >= CURDATE() AND create_time < CURDATE() + INTERVAL 1 DAY AND type = 1; |
|
1 2 3 4 5 6 |
SELECT type, IFNULL(ROUND(SUM(money), 2), 0.00) AS total_money FROM sys_balance WHERE status = 1 GROUP BY type; |
财务数据对精度敏感,统一SQL层使用ROUND规范处理,能极大减少前后端因小数保留规则不一致产生的对账问题。