一次有意思的number类型异常

事件背景

       某天某月某日,阳光明丽,万里无云,博主正专心在抗战一线撸码,突然蹦的一声,锅从天上来,“报表数据核对不上”,运营小姐姐捉急等着出数据呢,博主二话不说,撸起袖子带着火眼金睛直接干,用火眼排除了大半天硬是没发现问题。后经过在度娘的各种点拨下,突然闪过一道亮光,问题最终得到解决。

现象描述

数据库:oracle
A表的amount字段类型为number(15)
B表的amount字段类型为number(15)
执行sql查询语句:
select t1.amount,t2.amount from A t1, B t2 where t1.id = t2.id and t1.amount <> t2.amount and t1.create_time=‘XXXXX’;
执行结果:
在这里插入图片描述
哎呦,这是个什么梗,类型也一样,条件是<> 为何显示出来的amount都是500069 难道是博主老了?出现幻觉了?

排查过程

执行sql:select dump(amount),amount from A where id = 1 and create_time=‘XXXXX’;
在这里插入图片描述
执行sql:select dump(amount),amount from B where id = 1 and create_time=‘XXXXX’;
在这里插入图片描述
啪啪。打脸了。两个存储结果还不一样,这就好比java中,看到两个对象equal一样,对象hashcode不一样,结果不是同一个对象一样的道理,通过以上可以看到,实际存储的数据并不同,那到底是哪个有问题呢?

dump内容的含义

Type 1=Varchar2 ,2=Number, 12=Date , 96=Char
Len 表示占用的字节数
符号/指数位,数字1,数字2,数字3,数字N

符号/指数位: 最高位为1表示正数
正数:加 1 存储数字
负数:被 101 减数字 (如果总长度小于21个字节,最后加一个102,是为了排序的需要)

正数指数=符号/指数位 - 193
负数指数=62 - 符号/指数位

是不是很懵逼,官方语言一直都是那么难理解?

接下来。我们举个例子:
select dump(123456.789) from dual;
Typ=2 Len=6: 195,13,35,57,79,91

符号/指数位=195 正数
指数=195 - 193 = 2
数字1:13 - 1 = 12 * 1002= 120000        指数2 = 2-0
数字2:35 - 1 = 34 * 1001 = 3400           指数1 = 2-1
数字3:57 - 1 = 56 * 1000 = 56               指数0 = 2-2
数字4:79 - 1 = 78 * 100-1 = 0.78           指数-1 = 2-3
数字5:91 - 1 = 90 * 100-2 = 0.009         指数-2 = 2-4
120000 + 3400 + 56 + 0.78 + 0.009 = 123456.789

select dump(-123456.789) from dual;
Typ=2 Len=7: 60,89,67,45,23,11,102
符号/指数位:60 负数
指数=62-60=2

数字1:101- 89 = 12 * 1002= 120000        指数2 = 2-0
数字2:101 - 67 = 34 * 1001 = 3400           指数1 = 2-1
数字3:101- 45 = 56 * 1000 = 56               指数0 = 2-2
数字4:101 - 23 = 78 * 100-1 = 0.78           指数-1 = 2-3
数字5:101 - 11= 90 * 100-2 = 0.009         指数-2 = 2-4
120000 + 3400 + 56 + 0.78 + 0.009 = 123456.789

还有一个102没有被计算,Oracle是为了排序的需要:
-123456.789在数据库中实际存储为:60,89,67,45,23,11
而-123456.78901在数据库中实际存储为:60,89,67,45,23,11,91
-123456.789在数据库内部的存储值如果不加102,在排序上会出现-123456.78901 > -123456.789的情形

有了这些基础,那我们来看出现的问题
select dump(500069) from dual;
Typ=2 Len=4: 195,51,1,70
50 * 1002=500000
0 * 1001=0
69*1000=69
结果相加=500000+0+69=500069

Typ=2 Len=10: 195,51,1,69,100,100,100,100,100,41
50 * 1002=500000
0 * 1001=0
68 * 1000= 68
99 * 100-1= 0.99
99 * 100-2= 0.0099
99 * 100-3= 0.000099
99 * 100-4= 0.00000099
99 * 100-5= 0.0000000099
40 * 100-6= 0.00000000004
结果相加=500068.99999999994

是不是很surprise
虽然显示一样的数字,但是Oracle内部存储数据是不同的,所以进行比较是被认定为不同的
col1实际上是有精度的数字500068.99999999994,col2在插入的时候转为了整数500069

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值