update后小数位被截断是存储层按列定义(如decimal(10,2))强制截断所致,并非显示问题;ora-01438因值超出number(p,s)范围;mybatis中需显式指定jdbctype和numericscale;浮点类型误差属ieee 754固有缺陷,须改用decimal并确保应用层用bigdecimal/decimal.decimal。

UPDATE后小数位被截断或四舍五入了
这不是“显示问题”,而是值在写入时已被数据库按列定义截断。例如目标列为DECIMAL(10,2),你UPDATE 3.14159,实际存入的是3.14——SQL Server/Oracle/MySQL都会照规则执行,不报错也不警告。
验证方法很简单:SELECT col, SQL_VARIANT_PROPERTY(col, 'Scale') AS scale FROM t WHERE ...(SQL Server)或查information_schema.columns确认当前列的numeric_scale;再用CAST(col AS CHAR)看字符化结果是否已失真。
- 如果
CAST出来就是3.14,说明存储层已丢精度,修复必须改列定义或重写数据 - 如果
CAST出来是3.14159但SELECT显示为3.14,那是客户端或BI工具做了格式化,不是数据库问题
ORA-01438错误:数值超出列精度范围
Oracle报这个错,代表你试图INSERT/UPDATE的值超出了NUMBER(p,s)定义的整数位或小数位。比如列是NUMBER(5,2),最大允许999.99,而你传了1000.00或999.999。
别急着改应用代码,先定位源头:
- 查触发该操作的SQL语句,确认传入值来源:是应用硬编码?计算字段?还是ETL脚本生成?
- 运行
SELECT column_name, data_precision, data_scale FROM user_tab_columns WHERE table_name = 'YOUR_TABLE' AND column_name = 'YOUR_COLUMN' - 用
ROUND(value, s)或TRUNC(value, s)临时兜底,但注意ROUND可能引发业务逻辑偏差(如税费计算)
长期解法只有两个:要么扩大列精度(ALTER TABLE ... MODIFY column NUMBER(7,3)),要么在应用层做校验拦截——后者更安全,避免历史数据被隐式截断。
MyBatis + SQL Server MERGE INTO中DECIMAL精度丢失
这个问题常发生在批量MERGE场景,表面是UPDATE后精度不对,根因其实是JDBC参数绑定时没声明精度,驱动自动降级为低精度BigDecimal或截断小数位。
关键动作就一条:在#{}里显式加jdbcType和numericScale:
<foreach collection="list" item="item" separator=",">
(#{item.amount}, #{item.rate, jdbcType=DECIMAL, numericScale=4})
</foreach>
同时检查三处是否对齐:
- 数据库目标列定义,如
rate DECIMAL(18,4) - JDBC连接串是否含
sendDecimalAsBigDecimal=true - MyBatis日志是否打印出绑定参数为
BigDecimal: 12.3456(而非Double: 12.345600000000001)
浮点类型字段UPDATE后出现0.30000000000000004这类误差
只要字段类型是FLOAT或DOUBLE,这误差就必然存在——它不是BUG,是IEEE 754标准的固有表现。你UPDATE 0.1 + 0.2,存进去的就是近似值。
修复路径很明确:不能靠ROUND()函数掩盖,得从源头换类型:
- 立刻停用
FLOAT存金额、比率、配置参数等需精确值的字段 - 用
ALTER TABLE ... ALTER COLUMN col TYPE DECIMAL(18,6)(PostgreSQL)或ALTER COLUMN col DECIMAL(18,6)(SQL Server)迁移 - 迁移前用
CAST(col AS DECIMAL(18,6))校验旧数据是否可无损转换(若原值已是浮点误差累积结果,CAST只能固化当前近似值)
最易被忽略的点:即使改了字段类型,如果应用代码仍用double接收再UPDATE,误差会在应用层重新引入——Java必须用BigDecimal,Python用decimal.Decimal,且构造时传字符串而非float字面量。











