oracle數值類型漫談
這片博客本來想的是只寫pls_integer與number之間的性能比較,但是提筆之后我又想寫更多我的思考過程,于是索性寫一篇關于oracle數值類型的文章。干貨太多,未免看得乏味,本篇我嘗試用一種聊天談話的方式行文,希望同行者可以在一種輕松的氛圍中和我一起來探討oracle的諸多數值類型,好吧,那我就先拋磚引玉了。
1、number
我最先接觸的數值類型就是老大哥number,它定義為number(p,s),p是精度,最大38位,s是刻度范圍,可在-84~127間取值。舉個栗子(例子):number(4,2),指的就是小數點整數+小數部分一共有4位,不包含小數點位,也就是說整數部分最多兩位,如果小數部分多于2位,會做四舍五入處理(對于sqlserver和
mysql也會做四舍五入處理),如果小數部分不足2位的話,則會補0(對于sqlserver也會補0,但是mysql不會),如果不寫p,s,那么就默認為38。number類型是oracle最常用的數字類型了,在很長一段時間之內,我以為數值類型就只有number,因為number類型足以處理幾乎全部的數字類型了。但是之后在oracle道路上繼續行走的過程中,我又認識了其他衍生的數值類型。
2、integer
其實最開始看到這個數值類型的時候,同很多人一樣,我也認為它是number類型的子類型,最大38位,對于小數做自動四舍五入處理,但是后來我發現不完全是這樣的。作為number的子類型不假,一般情況下,integer是作為存儲整數值存在的,在需要存儲整數值的時候我們會想到這個數值類型,但是它的最大位可不是38位,在插入的測試數值看到,它已經遠遠超過38位了,如圖:
可以看到,對于integer只能存儲38位的言論可謂是直接推翻。接下來我們來看坊間盛傳的,"integer只能存儲整數,對于小數會做四舍五入處理",來看一個例子:
create or replace procedure integer_test(a integer)
as
b integer := a;
begin
dbms_output.put_line(b);
end;
輸出結果如圖:
看到這個結果的時候,相信很多同學都已經涼了,簡直是顛覆了以往的認識啊,的確,從這個例子可以看出,integer是number的子類型,但是它不會強制進行四舍五入處理,同時它最大位數大于38位。
3、int,numeric,decimal
這三種數值類型是oracle為了兼容其他數據庫而存在的,同樣都是number的子類型。在進行類型聲明的時候,如果直接聲明為int,numeric,decimal,它會被直接存為integer,處理方式同integer一模一樣;numeric和decimal如果帶有精度和刻度范圍,那么會被存為number(p,s),處理方式同number(p,s),int類型只能被聲明為int,不能帶精度和刻度范圍,所以只能當作integer來處理。
4、float
float也是number的子類型,float(n),n指的是精度,是二進制精度,這里可不是十進制精度哦,計算公式為:binary precision=ceil(b*0.30103)
,float沒有刻度范圍.n的范圍為1~126。那么怎么計算存入數據庫中的數值呢,比如:float(2),那么它的精度是ceil(2*0.30103)=1,如果存入13.5,那么應該是1.35*10^1,精度為1,1.35四舍五入變為1,所以就是1*10^1=10存入的結果就是10;如果是19.5,那么應該是1.95*10^1,精度為1,1.95四舍五入為2,就是2*10^1,結果存入20。如果沒有精度,那么映射為number類型,直接存入此值。
oracle沒有直接為double類型的數值類型。
5、binary_integer和pls_integer
這個數字類型只能用于PL/SQL,他們相比number來說更具優勢,首先是占有較少的存儲空間,其次他們是直接通過硬件來計算的,也就是直接通過CPU進行計算,而不用經過轉換,number類型在計算的時候,還要先被轉換為二進制,所以在計算速度上,binary_integer和pls_integer比number性能好很多,建議在PL/SQL中使用這兩種數據類型來代替number,你將會得到很大的驚喜。
5、binary_float和binary_double
這兩種數值類型10g之后才出現,binary_float可以存儲一個單精度的32位浮點數,binary_double可以存儲一個雙精度的64位浮點數,
binary_float和binary_double占有更少的存儲空間,同樣的,binary_float和binary_double也是直接通過硬件計算的,所以它的計算效率更高,而且它們比起number來說可以存儲更大或者更小的數值,但是這兩種數值類型也會存在精度不準的情況。
6、寫到浮點型,我需要說一說浮點型存數不精確的原因:
首先,我們要知道浮點型在計算機是怎么存儲的,比如:要存入13.75,首先將整數部分轉為二進制,13轉為二進制是1101,接下來將小數部分轉為二進制,小數部分是0.75,將小數部分乘以2,取結果的整數部分為二進制的一位,然后繼續取結果的小數部分乘以2重復,一直到小數部分全部為0結束,在這個過程中,是會出現循環乘不盡的情況,但是因為浮點數的小數位是確定的(float是23位,double是52位),所以到了指定的小數位就會停下來,這就是浮點數存數不精確的原因,可以看到如果可以在小數位范圍之內乘盡的話,那么結果就存儲的準確了。來看看小數部分(0.75)轉為二進制的過程:
0.75*2=1.5 取1
0.5*2=1 取1
乘到這里,即0.5*2=1,1已經沒有小數部分了,所以到這里也就停止了,最后的結果是0.11,與上面整數部分合起來,那么13.75轉換為二進制為1101.11,即1.10111*2^3,1.10111是小數部分,2為底,6為指數部分,這個1.10111*2^3就是轉換好的二進制浮點數,接著就是將這個二進制浮點數存入計算機了。
將其存入計算機內,將會使用浮點表示法,分為三大部分:
第一部分:符號位,占用1位,用來區分正負數,0為正數,1為負數;對于1.10111*2^3,它是正數,所以符號位是0
第二部分:指數位,float占用8位,double占用11位,用來表示指數;對于1.10111*2^3,指數位是3
第三部分:小數位,float占用23位,double占用52位,用來表示小數,不足位數補0;對于1.10111*2^3,小數位是10111
所以對于float,符號位+指數位+小數位=1+8+23=32位,對于double,符號位+指數位+小數位=64位,同時可以看到,指數位決定了大小范圍,小數位決定了精度。因為指數位有可能是正數,也有可能是負數,負數算起來比正數麻煩,所以為了減少計算的麻煩,在存儲指數的時候,需要將它存儲位無符號的整數,所以在指數位上會加一個偏移量,float的偏移量是127,double的偏移量是1023,如果需要轉換為十進制的話,到時候指數減去相應的值就可以了。那么對于13.75(float),本來它的二進制指數值是3,現在就成了3+127=130,轉換為二進制是10000010,最終:13.75(float)被存儲為:符號位 指數位 小數位(不足位補0)=0 10000010 10111 00000 00000 00000 000
7、萬丈高樓平地起,有時候我們太過追求一些高級語法,或者高大尚的東西,反而在基礎的東西上掌握不牢,實際上,數據庫的基礎數據類型往往沒有表面上看的那么簡單,其實任何語言都是一樣,只有掌握好基礎知識,做一個基礎精通者,那么,在我們進階的道路上就會水到渠成。寫到最后,才發現整體下來,整篇文章也沒有那么輕松,嚴肅的東西寫慣了,一下子不太適應寫輕松的東西,沒事,能拋磚引玉就好。