作者:dbtan |【转载时请以超链接形式标明文章出处作者信息】


数值在Oracle的内部存储:

Oracle在数据库内部通过相应的算法转换来进行数据存储,这里简单介绍Oracle的Number型数值存储及转换。可以通过DUMP函数来转换数字的存储形式,一个简单的输出类似如下格式:

sys@NEI> select dump(1),dump(1,16) from dual;
DUMP(1)            DUMP(1,16)
------------------ -----------------
Typ=2 Len=2: 193,2 Typ=2 Len=2: c1,2

DUMP函数的输出格式类似:

类型 <[长度]>,符号/指数位 [数字1,数字2,数字3,……,数字20]

其中各参数的含义如下:

·类型指字段数据类型,Number型,Type=2(类型代码可以从Oracle的文档上查到);
·长度指存储的字节数;
·符号/指数位用于代表数字的正负及指数值;
·数据存储。

在存储上,Oracle对正数和负数分别进行存储转换。而0既不属于正数,也不属于负数,是单独进行处理的。

sys@NEI> select dump(0),dump(0,16) from dual;
DUMP(0)          DUMP(0,16)
---------------- ---------------
Typ=2 Len=1: 128 Typ=2 Len=1: 80

在以上的输出中,0以十六进制0x80存储,仅包含了符号位,未包含数据位。为什么用0x80存储呢?注意0x80其二进制表示为1000 0000,2Bytes如果同时用来表示正数和负数,那么0x80正好将编码进行了等分,所以0x80用来表示0数值。

有了0值划分之后,符号位大于80的就是正数,符号位小于80的就是负数。接下来看看数值的存储,以下是数值1的存储输出:

sys@NEI> select dump(1),dump(1,16) from dual;
DUMP(1)            DUMP(1,16)
------------------ -----------------
Typ=2 Len=2: 193,2 Typ=2 Len=2: c1,2

数值1的符号位为193,表示这是一个正数,而1使用的是数值2来存储。这是因为在C语言中0作为字符串终结符被保留,所以数值1使用2存储,进一步地,所有的正数都是通过“加1”进行存储表示的。

简单地说,正数以及负数安装如下规则存储。

·正数:加1存储。
·负数:被101减。

接下来再来看看指数位的作用,根据科学计数法可以知道,任何一个实数S都可以描述为A.B×10^n的形式(A表示整数部分,B表示小数部分,而n表示10的指数部分)。当S大于1时,N大于等于0,S小于1时,N小于0。

存储中的指数位可以通过如下方式换算出来。

·正数:指数=符号/指数位-193(0xC1)
·负数:指数=62(0x3E)- 第一字节

去除指数位,实际上从<数字 1>开始才是有效的数据位,根据指数及第一位的数值就可以将存储的数值还原出来,所以Oracle存储的计算方法为:

∑<数据位n>×100^(指数-N)
注意这里N是有效位数的顺序位,第一个有效位的N=0。

来看一下以下的示例说明:

sys@NEI> select dump(123456.789) from dual;
DUMP(123456.789)
-------------------------------
Typ=2 Len=6: 195,13,35,57,79,91

<指数位>=195 - 193 = 2
<数字1>=13 - 1 = 12 *100^(2-0) = 120000
<数字2>=35 - 1 = 34 *100^(2-1) = 3400
<数字3>=57 - 1 = 56 *100^(2-2) = 56
<数字4>=79 - 1 = 78 *100^(2-3) = .78
<数字5>=91 - 1 = 90 *100^(2-4) = .009
    ∑ = 123456.789

sys@NEI> select dump(-123456.789) from dual;
DUMP(-123456.789)
----------------------------------
Typ=2 Len=7: 60,89,67,45,23,11,102
<指数位>=62 - 60 = 2
<数字1>=101 - 89 = 12 *100^(2-0) = 120000
<数字2>=101 - 67 = 34 *100^(2-1) = 3400
<数字3>=101 - 45 = 56 *100^(2-2) = 56
<数字4>=101 - 23 = 78 *100^(2-3) = .78
<数字5>=101 - 11 = 90 *100^(2-4) = .009
    ∑ = 123456.789(-)

注意在负数末尾会加入102,也就是十六进制的0x66作为标记,这可以被看作符号位,表示负数,同时也是为了排序的需要,-123456.789在数据库中实际存储为60,89,67,45,23,11,而-123456.78901在数据库中实际存储为60,89,67,45,23,11,91,可见,如果不在最后加上102,在排序时会出现 -123456.789 < -123456.78901的情况。由于正数的表示范围是0x01~0x64,负数的表示范围是0x65~0x02。因此,不会在表示数字时出现0x66的。

- The End -