Oracle 21c中Number数据类型工作机制及ORA-01438错误相关疑问咨询
首先,咱们得把 Oracle 中 NUMBER(p, s) 的两个核心参数讲透,这是理解所有问题的关键:
- p(精度):指数字的有效数字总位数(从第一个非零数字开始数,正负号和小数点不计入),范围是 1~38。
- s(刻度):指小数点右侧的位数,范围是 -84~127。正数表示保留的小数位数,负数表示小数点左侧的位数会被四舍五入到指定的 10 的幂次。
针对正数刻度的场景(也就是你遇到的情况),还有个关键规则:
当 s > p 时,意味着这个字段只能存储绝对值小于 10^-(s-p) 的数字——换句话说,小数点后前 s-p 位必须是 0,剩下的 p 位是有效数字。比如 NUMBER(1,2),s-p=1,所以只能存储 -0.09 ~ 0.09 之间的数;NUMBER(2,3),s-p=1,只能存储 -0.099 ~ 0.099 之间的数。
现在咱们逐个分析你的测试案例:
1. 为什么 select cast (0.8 as number(1,2)) from dual; 报错?
NUMBER(1,2) 要求数字绝对值小于 10^-(2-1)=0.1,也就是只能是 -0.09 ~ 0.09 之间的数。而 0.8 的绝对值是 0.8,远大于 0.1,直接超出了该类型的存储范围,所以触发 ORA-01438 错误。
2. 为什么 select cast (0.81 as number(1,2)) from dual; 也报错?
和上面的原因一致:0.81 的绝对值 0.81 > 0.1,不符合 NUMBER(1,2) 的范围限制;同时它的有效数字是 2 位,也超过了 p=1 的精度要求,双重触发错误。
3. 为什么 select cast (0.81 as number(2,2)) from dual; 可以正常运行?
NUMBER(2,2) 中 p-s=0,表示整数部分必须为 0,小数部分保留 2 位,且有效数字总位数不超过 2。0.81 刚好满足:整数部分是 0,小数部分 2 位,有效数字是 2 位(8 和 1),完全符合规则,所以可以正常转换。
4. 为什么 select cast (0.81 as number(2,3)) from dual; 不行?
NUMBER(2,3) 中 s-p=1,要求数字绝对值小于 10^-1=0.1,也就是只能是 -0.099 ~ 0.099 之间的数。0.81 的绝对值是 0.81,远大于 0.1,超出了范围,因此报错。
最后回到你最初的表结构问题:你定义的 PERC NUMBER(1,2) 只能存储 0.09 及以下的小数,而你插入的 0.8 和 0.2 都超出了这个范围,所以触发错误。改成 NUMBER(1,1) 后,p-s=0,允许存储 -0.9 ~ 0.9 之间的数,0.8 和 0.2 都在这个范围内,问题自然解决。
内容的提问来源于stack exchange,提问作者user17795768

