Oracle可以兼容多种存储数值的数据类型,如integer,number,decimal,numberic,float,double,binary_float和binary_float等,但推荐使用NUMBER类型,因为NUMBER几乎可以表示任何复杂的数值数据,也建议在数据库开发规范中明确统一使用NUMBER数据类型来表示数值。
NUMBER类型的用法如下:
定义格式NUMBER(precision,scale)
precision表示数字中的有效位(从左边第一个不为0的数算起,小数点和负号不计入有效位数),取值范围从1到38 默认38
scale表示精确到多少位,取值范围是从-84到127 默认值取决于precision,如果没有指定precision,那么s是最大范围,如果指定了precision,那么s=0
大于零时,表示数字精确到小数点右边的位数;小于零时,将把该数字取舍到小数点左边的指定位数
所以,NUMBER整数部分允许的长度为(precision- scale),无论scale是正数还是负数,
如果precision小于scale,表示存储的是没有整数的小数
关于number p s参数遵循以下规则:
当一个数的整数部分的长度> p-s 时,Oracle就会报错
当一个数的小数部分的长度> scale时,Oracle就会舍入
当scale为负数时,Oracle就对小数点左边的s个数字进行舍入
当scale> precision时, p表示小数点后第s位向左最多可以有多少位数字,如果大于p则Oracle报错
看一下举例:
上图中有一处错误哦 大家能看出来吗?
我们来看一个因为使用NUMBER类型不当造成问题的案列:
selectt1.amount,t2.amount,t1.check_key from t_XXX_bizaction t1, t_XXX_mercht2 where t1.check_key = t2.check_key
andt1.amount <> t2.amount
andt1.trade_stat=t2.trade_stat
andt1.check_key = '150101151222305822983'
andt1.check_external_stat = 'S0F';
查询中条件是<> 为何显示出来的amount都是500069呢?我们来dump一下看一下信息
select dump(amount),amount from t_XXX_bizaction wherecheck_key = '150101151222305822983';
selectdump(amount),amount from t_XXX_merch where check_key ='150101151222305822983';
dump内容的含义为:
Type1=Varchar2 2=Number 12=Date 96=Char
Len 表示占用的字节数
符号/指数位,数字1,数字2,数字3,*
符号/指数位: 最高位为1表示正数
正数:加 1 存储数字
负数:被 101 减数字 (如果总长度小于21个字节,最后加一个102,是为了排序的需要)
正数指数=符号/指数位 - 193
负数指数=62 - 符号/指数位
举例:
selectdump(123456.789) from dual;
Typ=2 Len=6: 195,13,35,57,79,91
符号/指数位=195 正数
指数=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] = 0.78
数字5:91 - 1 = 90 * 100^[2 - 4] = 0.009
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 * 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] = 0.78
数字5:101 - 11 = 90 * 100[2 - 4] = 0.009
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的情形
我们继续看案列中的数据:
selectdump(500069) from dual;
Typ=2Len=4: 195,51,1,70
50*100^2=500000
0*100^1=0
69*100^0=69
500000+0+69=500069
另外一个呢
Typ=2 Len=10: 195,51,1,69,100,100,100,100,100,41
50*100^2=500000
0*100^1=0
68*100^0=68
99*0.01=0.99
99*0.0001= 0.0099
99*0.000001= 0.000099
99*0.00000001= 0.00000099
99*0.0000000001= 0.0000000099
40*0.000000000001=0.00000000004
结果=500068.99999999994
select dump(500068.99999999994) from dual;
Typ=2 Len=10: 195,51,1,69,100,100,100,100,100,41
由于两个表的number类型不一致导致的问题,t_XXX_merch的amount字段类型是number,
t_XXX_bizaction的amount字段类型是number(15) ,number(15)是无法存储小数部分的
我们可以做个测试复盘这个问题:
create table test_number(col1 number,col2 number(15));
insert into test_number values (500068.99999999994,500068.99999999994);
commit;
select col1,dump(col1) dump1,col2,dump(col2) dump2from test_number;
COL1 DUMP1 COL2DUMP2
---------- -------------------------------------------------- ----------------------------------------
500069 Typ=2Len=10: 195,51,1,69,100,100,100,10 500069 Typ=2 Len=4: 195,51,1,70
0,100,41
select* from test_number where col1<>col2;
COL1 COL2
---------- ----------
500069 500069
显示一样,但是Oracle内部进行比较是不同的,col1实际上是有精度的数字
所以在生产系统中,建议使用相同的number类型,一般最好使用整数,涉及金额的可以以分为单位。