oracle數值型別漫談

tangguowuvv發表於2018-03-14
這片部落格本來想的是隻寫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、萬丈高樓平地起,有時候我們太過追求一些高階語法,或者高大尚的東西,反而在基礎的東西上掌握不牢,實際上,資料庫的基礎資料型別往往沒有表面上看的那麼簡單,其實任何語言都是一樣,只有掌握好基礎知識,做一個基礎精通者,那麼,在我們進階的道路上就會水到渠成。寫到最後,才發現整體下來,整篇文章也沒有那麼輕鬆,嚴肅的東西寫慣了,一下子不太適應寫輕鬆的東西,沒事,能拋磚引玉就好。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30485601/viewspace-2151857/,如需轉載,請註明出處,否則將追究法律責任。

相關文章