设为首页 收藏本站
查看: 424|回复: 0

[经验分享] Oracle基本数据类型存储格式浅析(二)——数字类型

[复制链接]

尚未签到

发表于 2016-8-6 10:52:35 | 显示全部楼层 |阅读模式
这篇文章主要描述NUMBER类型的数据和如何在数据库中存储的。


Oracle的NUMBER类型最多由三个部分构成,这三个部分分别是最高位表示位、数据部分、符号位。其中负数包含符号位,正数不会包括符号位。另外,数值0比较特殊,它只包含一个数值最高位表示位80,没有数据部分。


正数的最高位表示位大于80,负数的最高位表示位小于80。其中一个正数的最高位是个位的话,则最高位表示位为C1,百位、万位依次为C2、C3,百分 位、万分为依次为C0、BF。一个负数的最高位为个位的话,最高位表示位为3E,百位、万位依次为3D、3C,百分位、万分位依次为3F、40。


数据部分每一位都表示2位数。这个两位数可能是从0到99,如果是数据本身是正数,则分别用二进制的1到64表示,如果数据本身是负数,则使用二进制65到2表示。


符号位用66表示。


上面的这些是我通过DUMP结果总结出来的,对于上面提到的这些关系常数,Oracle之所以这样选择是有道理的,我们后面根据例子也可以推导出来,而且会进一步说明为什么会采用这种方式表示。这里列出的意思是使大家先对NUMBER类型数据有一个大概的了解。


下面我们通过一个例子详细说明:


  





SQL> CREATE TABLE TEST_NUMBER (NUMBER_COL NUMBER);


表已创建。


SQL> INSERT INTO TEST_NUMBER VALUES (0);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (1);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (2);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (25);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (123);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (4100);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (132004078);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (2.01);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (0.3);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (0.00000125);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (115.200003);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (-1);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (-5);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (-20032);


已创建 1 行。


SQL> INSERT INTO TEST_NUMBER VALUES (-234.432);


已创建 1 行。


SQL> COMMIT;


提交完成。


SQL> COL D_NUMBER FORMAT A50
SQL> SELECT NUMBER_COL, DUMP(NUMBER_COL, 16) D_NUMBER FROM TEST_NUMBER;


NUMBER_COL D_NUMBER
---------- --------------------------------------------------
          0 Typ=2 Len=1: 80
          1 Typ=2 Len=2: c1,2
          2 Typ=2 Len=2: c1,3
         25 Typ=2 Len=2: c1,1a
        123 Typ=2 Len=3: c2,2,18
       4100 Typ=2 Len=2: c2,2a
  132004078 Typ=2 Len=6: c5,2,21,1,29,4f
       2.01 Typ=2 Len=3: c1,3,2
         .3 Typ=2 Len=2: c0,1f
  .00000125 Typ=2 Len=3: be,2,1a
115.200003 Typ=2 Len=6: c2,2,10,15,1,4
         -1 Typ=2 Len=3: 3e,64,66
         -5 Typ=2 Len=3: 3e,60,66
     -20032 Typ=2 Len=5: 3c,63,65,45,66
   -234.432 Typ=2 Len=6: 3d,63,43,3a,51,66


已选择15行。


  


     下面根据例子得到的结果,对每行进行说明。首先说明两点基本的。DUMP函数返回的TYPE=2表示DUMP的数据类型是NUMBER,LENGTH=N表示数值在数据库中存储的长度是N。


1.DUMP(0)的结果是0x80,在前面已经提到,0只有高位表示位,没有数据位。由于0的特殊,既不属于正数,也不属于负数,因此使用高位表示位用 80表示就足够了,不会和其它数据冲突,Oracle出于节省空间的考虑将后面数据部分省掉了。但是为什么Oracle选择0x80表示0呢?我们知道正 数和负数互为相反数,每个正数都有一个对应的负数。因此如果我们要使用编码表示数值,则表示正数和负数的编码应该各占一半,这样才能保证使Oracle表 示数据范围是合理的。而0x80的二进制编码是1000 0000,正好是一个字节编码最大值的一半,因此,Oracle选择0x80来表示0,是十分有道理的。


2.DUMP(1)的结果是0xc102,0xc1表示了最高位个位,0x2表示数值是1。首先,Oracle为什么用C1表示个位呢?其实,道理和刚才 的差不多。采用科学计数法,任何一个实数S都可以描述为A.B×10n,A表示整数部分,B表示小数部分,而N表示10的指数部分。当S大于1时,N大于 等于0,S小于1时,N小于0。也就是说,采用指数的方式表示,N大于0和N小于0的情况各占一半左右时,Oracle所表示的范围最广。因此, Oracle选择了C1表示个位是最高位的情况。


SQL> SELECT TO_CHAR(ROUND(TO_NUMBER('81', 'XXX') + (TO_NUMBER('FF', 'XXX') - TO_NUMBER('81', 'XXX') + 1)/2), 'XX') FROM DUAL;




TO_
---
  C1


  


为什么ORACLE使用0x2表示1,而不直接使用0x1表示1呢?Oracle每个字节表示2位数,因此对于这个2位数,出现的可能是0~99共100 种可能,问题出在0这里。Oracle底层是用C语言实现的,我们知道二进制0在C语言中用作字符串终结符,Oracle为了避免这个问题,因此使用了 0x1表示0,并依次类推,使用0x64表示99。


3.DUMP(2)的结果是0xc103。


4.DUMP(25)的结果是0xc11a。前面提到,数据部分是以2位为最小单位保存的。因此对于25来说,最高位表示位仍然是个位,个位上的值是25,根据上面推出的规则,25在存储为0xc11a。


SQL> SELECT TO_CHAR(25 + 1, 'xx') FROM DUAL;




TO_
---
  1a


  


5.DUMP(123)的结果是0xc20218。由于123最高为是百位,所以最高位表示位为0xc2,百位上是1,用0x02表示,个位上是23,用0x18表示。


6.DUMP(4100)的结果是0xc22a。


注意一点,如果数字最后数位上如果是0,Oracle出于节省空间的考虑不会存储。比如:4100只保存百位上的41,12000000只保存百位位上的12,512000只保存万位上的51和百位上的20。


7.DUMP(132004078)的结果是0xc5022101294f。最高位是亿位,因此用0xC5表示,亿位上是1用0x02表示,百位位上是32用0x21表示,万位上是0用0x01表示,百位上是40用0x29表示,个位上78用0x4F表示。


注意:中间数位上的0不能省略。


8.DUMP(2.01)的结果是0xc10302。最高位是个位用0xC1表示,个位上是2用0x03表示,百分位上是1用0x02表示。


注意:个位下面一位是百分位不是十分位。


9.DUMP(0.3)的结果是0xc01f。最高位是百分位,使用0xC0表示,百分位上是30用0x1F表示。


10.DUMP(0.00000125)的结果是0xbe021a。最高位是百万分位,用0xBE表示,最高位上的1用0x02表示,25用0x1a表示。


11.DUMP(115.200003)的结果是0xc20210150104。


12.DUMP(-1)的结果是0x3e6466。最高位个位,用0x3E表示,64表示个位上是1,66是符号位,表示这个数是负数。


负数和正数互为相反数,负数的最高位表示位和它对应的相反数的最高位相加的值是FF。1的最高位表示位是C1,-1的最高位表示位是3E。负数中1用64 表示。负数中的数值和它相反数的数据相加是0x66,也就是符号位。正数1用0x02表示,负数1用0x64表示,二者相加是0x66。负数多个一个标识 位,用0x66表示。由于正数的表示范围是0x01到0x64,负数的表示范围是0x65到0x02。因此,不会在表示数字时出现的0x66表示。


13.DUMP(-5)的结果是0x3e6066。0x3e表示最高位是个位,0x60表示个位上是5,0x66是符号标识位。0x3E加0xC1是0xFF。0x60加0x06的结果是0x66。


14.DUMP(-20032)的结果是0x3c63654566。最高位是万位,正数的万位是0xC3,因此负数的万位是0x3C。万位上是2,正数用 0x03表示,负数为0x63,百位上是0,正数用0x01表示,负数使用0x65表示,个位上是32,正数用0x21表示,负数使用0x45表示。 0x66是负数表示位。


15.DUMP(-234.432)的结果是0x3d63433a5166。


  


根据Oracle的存储特性,还可以推出Oracle的number类型的取值范围。


Oracle的concept上是这样描述的:


The following numbers can be stored in a NUMBER column:


Positive numbers in the range 1 x 10^-130 to 9.99...9 x 10^125 with up to 38 significant digits.


Negative numbers from -1 x 10^-130 to 9.99...99 x 10^125 with up to 38 significant digits.


Zero.


下面来推导出取值范围。


来看符号位,0xC1表示个位。


SQL> select to_number('ff', 'xxx') - to_number('c1', 'xxx') from dual;


TO_NUMBER('FF','XXX')-TO_NUMBER('C1','XXX')
-------------------------------------------
                                          62


由于Oracle是两位、两位存储的,因此最高位相当于62×2=124,而且最高位上最大值是99,因此正数的最大值为9.999……×10^125。


SQL> select to_number('c1', 'xxx') - to_number('80', 'xxx') from dual;


TO_NUMBER('C1','XXX')-TO_NUMBER('80','XXX')
-------------------------------------------
                                          65


最高位相当于65×2=130,因此正数的最小值为1×10^-130。


负数和正数在各使用了一半的编码,因此具有相同的极值范围。

出自:http://blog.itpub.net/post/468/9445

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-253621-1-1.html 上篇帖子: Oracle数据库常见真实问题处理步骤(四) 下篇帖子: Oracle数据导入导出的N种实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表