您的位置:首页 > 数据库 > Oracle

Oracle Number类型存储结构详解

2013-12-20 17:18 246 查看
最近研究histogram,需要了解下number的数据类型,我翻了我的老贴子(发表于 2005-1-12 09:35:03)
Number类型存储结构宝典
这个帖子居然0回复,我只能说......,我觉得挺好的,贴这里备查

前言
===================================================
在datafile dump Oracle的data block的时候,看见其它类型都比较容易懂,
唯一number类型看上去怪怪的,所以经过无数次试验和查看文档,终于搞懂了它的内部格式。Oracle的segment格式很容易搞定,其它数据类型你用dump的方式就可以看懂了,这样就可以编写自己的udl了,我没时间,哪位老兄有兴趣可以试试啊!呵呵

理论研究
===================================================

Oracle中只有一种number类型来表示整型和浮点型,Oracle的这个数字类型更加接近人类的自然认识,好象跟计算机内部表示没有关系。
Oracle中的数字类型采用的时类似100进制的表示法:

它有1到21个字节组成,第一字节表示符号和幂,后面是有效位,可以有0~20个字节,每个字节表示一位(是100进制的哟),而且如果是正数,每位要减1,才是本来的数,如果是负数,被101减,才是本来的数。
它的幂是100的幂,用第一字节的后7bit表示,有点类似反补码,就是把第1bit(字节中的第2bit)变反后,就是一个7bit的补码表示法
如果是负数,第1byte的后7bit是被变反的,而且总长不满21bye,会加102(0x66)再buffer的末尾

Oracle 编码的时候,应该是先把数进行规范化,左右移动小数点,每次移动两位,转换为:
0.9999999999999999999999999999999999999999*100^m,可存40个有效数,或
0.0999999999999999999999999999999999999999*100^m,可存39个有效数
然后对有效数,从小数点后,每次取两位编码
再对m进行编码,m是7bit补码,表示范围是100^-64~100^63=10^-128~10^126

表示范围的研究 
===================================================

最大的正数应该是:
0.9999999999999999999999999999999999999999*100^63
也就是
0.9999999999999999999999999999999999999999*10^126
也就是:
9.99999999999999999999999999999999999999*10^125,39个9
从结构上讲应该能保存40个9,不过试验的时候只能插入39个9,可能是Oracle做了一定的限制,
当时某些情况下是可以插入40个有效数字的,见后见试验。

最大的负数是:
-0.0999999999999999999999999999999999999999*100^-64
也就是
-0.0999999999999999999999999999999999999999*10^-128
也就是
-9.99999999999999999999999999999999999999*10^--130,39个9哟

不过这个数好象

常见的数据举例:
===================================================

insert into demo(id) values(0);
insert into demo(id) values(1);
insert into demo(id) values(2);
insert into demo(id) values(3);
insert into demo(id) values(-1);
insert into demo(id) values(-2);
insert into demo(id) values(-3);
insert into demo(id) values(10);
insert into demo(id) values(100);
insert into demo(id) values(1000);
insert into demo(id) values(-10);
insert into demo(id) values(-100);
insert into demo(id) values(-1000);
insert into demo(id) values(0.1);
insert into demo(id) values(0.01);
insert into demo(id) values(0.001);
insert into demo(id) values(-0.1);
insert into demo(id) values(-0.01);
insert into demo(id) values(-0.001);

SQL> col id for 99990.999 
SQL> select vsize(id),id,dump(id,16) xdump from demo

VSIZE(ID)         ID XDUMP
---------- ---------- ------------------------------
         1         0.000 Typ=2 Len=1: 80
         2         1.000 Typ=2 Len=2: c1,2
         2         2.000 Typ=2 Len=2: c1,3
         2         3.000 Typ=2 Len=2: c1,4
         3        -1.000 Typ=2 Len=3: 3e,64,66
         3     -   2.000 Typ=2 Len=3: 3e,63,66
         3        -3.000 Typ=2 Len=3: 3e,62,66
         2       10.000 Typ=2 Len=2: c1,b
         2     100.000 Typ=2 Len=2: c2,2
         2   1000.000 Typ=2 Len=2: c2,b
         3      -10.000 Typ=2 Len=3: 3e,5b,66
         3    -100.000 Typ=2 Len=3: 3d,64,66
         3  -1000.000 Typ=2 Len=3: 3d,5b,66
         2        0.100 Typ=2 Len=2: c0,b
         2        0.010 Typ=2 Len=2: c0,2
         2        0.001 Typ=2 Len=2: bf,b
         3       -0.100 Typ=2 Len=3: 3f,5b,66
         3       -0.010 Typ=2 Len=3: 3f,64,66
         3       -0.001 Typ=2 Len=3: 40,5b,66

极限值的研究
===================================================

insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,125) );
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,124) );
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,123) );
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,122) );
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,121) );
                                                                     
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,-121));insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,-122));
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,-123));
insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,-128));

insert into demo(id) values( 9.9999999000000000000000000009912345678912*power(10,-130));
                                                                     
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,125));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,124));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,123));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,122));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,121));
                                                                     
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,-121));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,-122));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,-123));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,-128));
insert into demo(id) values(-9.9999999000000000000000000009912345678912*power(10,-130));

SQL>col id for 9.999999999999999999999999999999999999999EEEE
SQL> select vsize(id) sz,id from demo;

SZ                                              ID
--- -----------------------------------------------
21  9.999999900000000000000000000991234567890E+125
21  9.999999900000000000000000000991234567890E+124
21  9.999999900000000000000000000991234567890E+123
21  9.999999900000000000000000000991234567890E+122
21  9.999999900000000000000000000991234567890E+121
21  9.999999900000000000000000000991234567890E-121
21  9.999999900000000000000000000991234567890E-122
21  9.999999900000000000000000000991234567890E-123
21  9.999999900000000000000000000991234567890E-128
21  9.999999900000000000000000000991234567890E-130
21 -9.999999900000000000000000000991234567890E+125
21 -9.999999900000000000000000000991234567890E+124
21 -9.999999900000000000000000000991234567890E+123
21 -9.999999900000000000000000000991234567890E+122
21 -9.999999900000000000000000000991234567890E+121
21 -9.999999900000000000000000000991234567890E-121
21 -9.999999900000000000000000000991234567890E-122
21 -9.999999900000000000000000000991234567890E-123
21 -9.999999900000000000000000000991234567890E-128
21 -9.999999900000000000000000000991234567890E-130

看它们的内部结构为:
SQL>select dump(id,16) xdump from demo
XDUMP
--------------------------------------------------------------------------------------------
Typ=2 Len=21: ff,64,64,64,64,1,1,1,1,1,1,1,1,1,1,64,d,23,39,4f,5b
Typ=2 Len=21: ff,a,64,64,64,5b,1,1,1,1,1,1,1,1,1,a,5c,18,2e,44,5a
Typ=2 Len=21: fe,64,64,64,64,1,1,1,1,1,1,1,1,1,1,64,d,23,39,4f,5b
Typ=2 Len=21: fe,a,64,64,64,5b,1,1,1,1,1,1,1,1,1,a,5c,18,2e,44,5a
Typ=2 Len=21: fd,64,64,64,64,1,1,1,1,1,1,1,1,1,1,64,d,23,39,4f,5b
Typ=2 Len=21: 84,64,64,64,64,1,1,1,1,1,1,1,1,1,1,64,d,23,39,4f,5b
Typ=2 Len=21: 84,a,64,64,64,5b,1,1,1,1,1,1,1,1,1,a,5c,18,2e,44,5a
Typ=2 Len=21: 83,64,64,64,64,1,1,1,1,1,1,1,1,1,1,64,d,23,39,4f,5b
Typ=2 Len=21: 81,a,64,64,64,5b,1,1,1,1,1,1,1,1,1,a,5c,18,2e,44,5a
Typ=2 Len=21: 80,a,64,64,64,5b,1,1,1,1,1,1,1,1,1,a,5c,18,2e,44,5a
Typ=2 Len=21: 0,2,2,2,2,65,65,65,65,65,65,65,65,65,65,2,59,43,2d,17,b
Typ=2 Len=21: 0,5c,2,2,2,b,65,65,65,65,65,65,65,65,65,5c,a,4e,38,22,c
Typ=2 Len=21: 1,2,2,2,2,65,65,65,65,65,65,65,65,65,65,2,59,43,2d,17,b
Typ=2 Len=21: 1,5c,2,2,2,b,65,65,65,65,65,65,65,65,65,5c,a,4e,38,22,c
Typ=2 Len=21: 2,2,2,2,2,65,65,65,65,65,65,65,65,65,65,2,59,43,2d,17,b
Typ=2 Len=21: 7b,2,2,2,2,65,65,65,65,65,65,65,65,65,65,2,59,43,2d,17,b
Typ=2 Len=21: 7b,5c,2,2,2,b,65,65,65,65,65,65,65,65,65,5c,a,4e,38,22,c
Typ=2 Len=21: 7c,2,2,2,2,65,65,65,65,65,65,65,65,65,65,2,59,43,2d,17,b
Typ=2 Len=21: 7e,5c,2,2,2,b,65,65,65,65,65,65,65,65,65,5c,a,4e,38,22,c
Typ=2 Len=21: 7f,5c,2,2,2,b,65,65,65,65,65,65,65,65,65,5c,a,4e,38,22,c

到底能保存多少有效数字?
===================================================

Oracle说能保存39个有效数字,实际上它可以最多表示40个有效数字,但有时候只能保存39个,比如

insert into demo(id) values( 1234506789123450678912345067891234506789);
insert into demo(id) values(-1234506789123450678912345067891234506789);
查询
SQL>col id for 9.999999999999999999999999999999999999999EEEE
SQL> select id from demo;
+1.2345067891234506789123450678912345067890E+39
-1.2345067891234506789123450678912345067890E+39
这两行就有40个有效数字

负数为什么会在后面加0x66=102
===================================================

Oracle的number总的感觉是怪怪的,为什么?后来overtime提醒我,加0x66是排序需要,这时候我恍然大悟,原来我把排序这事个忘了,排序和检索对性能要求很高,你看看Oracle的number类型编码多复杂啊,要是索引和排序的时候需要把数据转换后再比较,那多慢啊!
回头一看,一切问题都明确了,Oracle第一字节前两bit,反来反去,原来也是排序需要,负数的有效数字被 101减,也是排序需要,末尾后面加102也是排序需要,因为当别人比它的有效数常的时候,一比较,别人大,实际上,它是负数,应该它自己大,所以加了个比0还小的数102,呵呵。

现在你看看前面的例子就知道了,只用按字符串排序,结果跟数字值的排序一样,真是巧妙啊!

我们可以得出如下得结论
===================================================

l        Oracle的number表示的范围非常大,精度也很高,远远超出计算机的内部表示,64bit整型和80bit的浮点型都远不及Oracle。实际上这么大的范围和精度,到计算机处理的时候,比如oci,occi程序,精度只能向下降低到计算机能表示的精度。
l        Number的存储空间为 1+UpperRound(有有效位数/2),如果是负数再加1,存储空间上和计算机内部格式差不多
l        Oracle表示的范围是按人类的自然数(十进制)来划分的,便于按需求设定精度,比如是-99~+99,而不是-128~127这样的范围
l        Oracle的number不是计算机的内部表示,而是要做一个很复杂的的类型转换,这肯定是要花很多cpu时间, 在做数学运算的时候,精度并没有丢失,说明Oracle的数学运算和函数都使用的是自己的number类型的,婴儿用品速度可想而知。在性能要求高的地方就不能直接使用number类型,
比如写一个性能要求高的触发器,如果涉及的数学运算比较多,应该改用oracle 的binary_integer类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: