不同数据库对round()舍入策略不一致导致聚合结果偏差:oracle和postgresql用银行家舍入,mysql和金仓用四舍五入;财务场景须逐笔舍入再聚合,并显式固化规则。

聚合结果不一致,不是数据错了,而是你没意识到不同数据库对同一句 SQL 的“理解”根本不同——它可能在 MySQL 里跑出 100.00,在 Oracle 里变成 99.99,而且两者都算“正确”。
ROUND() 舍入策略天生就不一样
同一个 ROUND(2.5, 0),Oracle 和 PostgreSQL 返回 2(银行家舍入),MySQL 和金仓返回 3(传统四舍五入)。这种差异单看无感,但放到 SUM(ROUND(x, 2)) 里,百万级交易后偏差可达千元级。更危险的是:如果你只在最终结果上 ROUND(SUM(x), 2),浮点累积误差会悄悄吃掉分位精度。
- 财务场景必须逐笔舍入再聚合:
SUM(ROUND(unit_price * qty * (1 - discount), 2)) - 别依赖数据库默认行为,显式写明规则,比如加注释“按银行家舍入保留两位”
-
ROUND()遇到NULL直接跳过,但若字段存了空字符串或非法字符(如'100.50a'),MySQL 可能静默转成0,Oracle 则报错——得提前用COALESCE(列, 0)和CAST(列 AS NUMERIC(18,2))
GROUP BY 不等于 ORDER BY,顺序乱是常态
不写 ORDER BY 的聚合结果,顺序完全不可预测。MySQL 5.7、PostgreSQL、Oracle 全都遵守 SQL 标准:GROUP BY 只分组,不排序。你看到的“有序”,只是某次执行碰巧走索引扫描,下次统计信息更新、并行线程调度一变,顺序就全乱。
- 错误写法:
SELECT dept, COUNT(*) FROM emp GROUP BY dept→ 结果顺序随时变 - 正确写法:
SELECT dept, COUNT(*) FROM emp GROUP BY dept ORDER BY dept -
NULL在排序中位置不统一:PostgreSQL 默认NULLS FIRST,Oracle 默认NULLS LAST,跨库迁移时得显式写ORDER BY dept NULLS LAST - 多字段排序必须能破歧义,比如
ORDER BY AVG(salary) DESC, emp_id,否则相同平均值的部门顺序不稳定
字符集与校对规则(COLLATION)隐式转换毁掉聚合
两个表 JOIN 或 GROUP BY 字段看着都是 VARCHAR(50),但一个用 utf8mb4_0900_as_cs,另一个是 utf8mb4_general_ci,数据库就会触发隐式转换——优化器无法走索引,改用全表扫描+临时表分组,结果不仅慢,还可能漏数据、重复计数。
- 查真实定义用:
SHOW FULL COLUMNS FROM t1 LIKE 'code',别信字段名 -
GROUP BY t1.name COLLATE utf8mb4_0900_as_cs是无效补丁,破坏索引命中,也改不了分组逻辑 - 唯一可靠解法是改字段定义:
ALTER TABLE t2 MODIFY code VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs - 外键字段、UNION 字段也得同步检查,它们不显式出现在 SQL 里,但照样参与字符比对
聚合函数语法等价,但语义和限制差很远
COUNT()、SUM() 这些基础函数虽都支持,但边界行为天差地别。比如 LISTAGG 在 Oracle 中强制要求 ORDER BY,否则报 ORA-01489;而 MySQL 的 GROUP_CONCAT 默认按插入顺序拼,超长静默截断,Oracle 超长直接报错。
-
LISTAGG(col, ',') WITHIN GROUP (ORDER BY col)是 Oracle 基本写法,ORDER BY不可省略 - MySQL 的
GROUP_CONCAT(DISTINCT col)在 Oracle 中得套子查询:LISTAGG(col, ',') WITHIN GROUP (ORDER BY col)包在SELECT DISTINCT外层 -
STATS_MODE()在 Oracle 是原生众数函数,MySQL 8.0+ 得靠窗口函数模拟,低版本只能子查询 +LIMIT 1,且多众数时结果不确定 -
COUNT(DISTINCT col)在 Oracle 12c+ 默认启用APPROX_COUNT_DISTINCT(快但有误差),MySQL 没这选项,全靠临时表硬算,大表极易卡死
真正难处理的不是某个函数写不对,而是这些差异层层嵌套:字符集不一致导致 JOIN 错漏 → 分组数据源已脏 → 再怎么 ROUND 或 ORDER BY 都只是把错误结果排整齐而已。最稳妥的做法,是在 ETL 层或视图定义里就把舍入规则、排序依据、字段类型全部固化,而不是指望应用层 SQL 临时打补丁。











