Oracle NUMBER本身不溢出,问题在于.NET/Java用窄类型(如decimal、long)硬转:C# decimal仅29位有效数字,而NUMBER(38)可达38位;应优先用OracleNumber或BigDecimal,转换前校验有效性,纯ID场景建议用字符串。
oracle 中的 number 本身不“溢出”,真正出问题的是你用窄精度变量接收、或在 .net/java 里用小范围类型(如 int、decimal)去硬转——这时候不是数据错了,是你读的方式错了。
为什么 NUMBER(38) 存整数会炸掉 C# decimal?
C# 的 decimal 最多支持 29 位有效数字,而 Oracle NUMBER(38) 可存满 38 位整数(比如雪花 ID、时间戳毫秒值)。一旦数据库里真有 99999999999999999999999999999999999999 这种值,调用 GetDecimal() 立刻抛 System.OverflowException: value was either too large or too small for a decimal。
这不是 Oracle 在报错,是 .NET 运行时拒绝把超长整数塞进 decimal 结构里。
- 别依赖
reader.GetDecimal(i),改用reader.GetOracleNumber(i) -
OracleNumber是 ODP.NET 提供的无损容器,能承载完整 38 位精度 - 转
decimal前必须检查:on.IsValid && on.IsFinite,否则仍可能崩 - 对纯大整数(如 ID 字段),直接
on.ToString()更安全,避免精度丢失
Java 里 ResultSet.getObject() 返回 BigDecimal 还是 long?
Oracle JDBC 驱动默认把 NUMBER 映射为 java.math.BigDecimal,这是对的——它能保精度。但很多老代码习惯写 rs.getInt("id") 或 rs.getLong("id"),这就埋雷了:
-
getInt():只取低 32 位,超了就静默截断(比如 2147483648 变成 -2147483648) -
getLong():上限是 ±9,223,372,036,854,775,808,但 OracleNUMBER(38)能到 ±10³⁸,照样溢出 - 正确姿势是
rs.getBigDecimal("id"),或明确需要整数时用bd.toBigInteger() - 如果字段确定是 ID 类型(只读、不计算),映射为
String反而最省心
PL/SQL 里赋值报 ORA-06502 VALUE_ERROR 怎么定位?
这不是数据问题,是变量声明太窄。例如:v_id NUMBER(10) := 12345678901;(11 位),运行就崩,错误信息只说 ORA-06502,不告诉你哪一行。
- 批量操作(
FORALL)必须加SAVE EXCEPTIONS,否则错一个整批回滚 - 捕获后查
SQL%BULK_EXCEPTIONS(i).ERROR_INDEX才知道是原数组第几个元素超了 - 但注意:
SAVE EXCEPTIONS捕不到 PL/SQL 变量赋值异常(比如上面那个v_id := ...),那种得靠缩小计算步长或改用NUMBER无精度声明 - 临时绕过法:
v_id NUMBER := 12345678901;(不写精度),让 Oracle 动态适配
EF Core 映射 NUMBER 到 decimal 为什么 SaveChanges 就崩?
EF Core 不懂 Oracle 的 NUMBER 有多野,它默认全当 decimal 处理。只要数据库里有一条记录的数值超出 C# decimal 范围,查询或保存时都会炸,且没 fallback 机制。
- 实体属性别写
public decimal Id { get; set; },换成public OracleNumber? Id { get; set; }(需引用Oracle.ManagedDataAccess.Core) - 如果只想读、不参与计算,用
ValueConverter存成字符串 - 若坚持用
decimal,必须在OnModelCreating里显式约束精度:property.HasColumnType("NUMBER(18,0)") - 生成迁移脚本前务必检查 DDL:默认
NUMBER会被建为NUMBER(38),不是你想要的NUMBER(10)
最常被忽略的一点:Oracle 的 NUMBER 是逻辑类型,没有固定字节长度;而 .NET 的 decimal 和 Java 的 long 是物理类型,有硬上限。处理前先想清楚——这个字段你到底要算,还是要存?要算就用高精度类型兜底,要存就别硬转,字符串最老实。











