Oracle 数据类型及存储方式
袁光東 原創(chuàng)?
概述?
通過實(shí)例,全面而深入的分析oralce的基本數(shù)據(jù)類型及它們的存儲(chǔ)方式。以O(shè)RACLE 10G為基礎(chǔ),介紹oralce 10g引入的新的數(shù)據(jù)類型。讓你對(duì)oracle數(shù)據(jù)類型有一個(gè)全新的認(rèn)識(shí)。揭示一些不為人知的秘密和被忽略的盲點(diǎn)。從實(shí)用和優(yōu)化的角度出發(fā),討論每種數(shù)據(jù)類型的特點(diǎn)。從這里開始o(jì)racle之旅!?
第一部份 字符類型?
§1.1? char?
定長(zhǎng)字符串,會(huì)用空格來填充來達(dá)到其最大長(zhǎng)度,最長(zhǎng)2000個(gè)字節(jié)。?
1. 新建一個(gè)測(cè)試表test_char.,只有一個(gè)char類型的列。長(zhǎng)度為10?
SQL> create table test_char(colA char(10));?
Table created?
2. 向這個(gè)表中插入一些數(shù)據(jù)。?
SQL> insert into test_char values('a');?
1 row inserted?
SQL> insert into test_char values('aa');?
1 row inserted?
SQL> insert into test_char values('aaa');?
1 row inserted?
SQL> insert into test_char values('aaaa');?
1 row inserted?
SQL> insert into test_char values('aaaaaaaaaa');?
1 row inserted?
注意:最多只能插入10個(gè)字節(jié)。否是就報(bào)錯(cuò)。?
SQL> insert into test_char values('aaaaaaaaaaa');?
insert into test_char values('aaaaaaaaaaa')?
ORA-12899: value too large for column "PUB_TEST"."TEST_CHAR"."COLA" (actual: 11, maximum: 10)?
3. 使用dump函數(shù)可以查看每一行的內(nèi)部存數(shù)結(jié)構(gòu)。?
SQL> select colA, dump(colA) from test_char;?
COLA?????? DUMP(COLA)?
---------- --------------------------------------------------------------------------------?
a????????? Typ=96 Len=10: 97,32,32,32,32,32,32,32,32,32?
aa???????? Typ=96 Len=10: 97,97,32,32,32,32,32,32,32,32?
aaa??????? Typ=96 Len=10: 97,97,97,32,32,32,32,32,32,32?
aaaa?????? Typ=96 Len=10: 97,97,97,97,32,32,32,32,32,32?
aaaaaaaaaa Typ=96 Len=10: 97,97,97,97,97,97,97,97,97,97?
注意:Typ=96 表示數(shù)據(jù)類型的ID。Oracle為每一種數(shù)據(jù)類型都進(jìn)行了編號(hào)。說明char類型的編號(hào)是96.?
Len =10 表示所在的內(nèi)部存儲(chǔ)的長(zhǎng)度(用字節(jié)表示)。雖然第一例只存了一個(gè)字符’a’,但是它還是占用了10個(gè)字節(jié)的空間。?
97,32,32,32,32,32,32,32,32,32 表示內(nèi)部存儲(chǔ)方式。可見oracle的內(nèi)部存儲(chǔ)是以數(shù)據(jù)庫(kù)字符集進(jìn)行存儲(chǔ)的。?
97正好是字符a的ASCII碼。?
可以使用chr函數(shù)把ASCII碼轉(zhuǎn)成字符。?
SQL> select chr(97) from dual;?
CHR(97)?
-------?
a?
要想知道一個(gè)字符的ASCII碼,可以使用函數(shù)ascii?
SQL> select ascii('a') from dual;?
ASCII('A')?
----------?
??????? 97?
32正好是空格的ascii碼值。?
Char類型是定長(zhǎng)類型。它總會(huì)以空格來填充以達(dá)到一個(gè)固定寬度。?
使用char類型會(huì)浪費(fèi)存儲(chǔ)空間。?
Oracle的數(shù)據(jù)類型的長(zhǎng)度單位是字節(jié)。?
SQL> select dump('漢') from dual;?
DUMP('漢')?
---------------------?
Typ=96 Len=2: 186,186?
可見一個(gè)漢字在oracle中是占用了兩個(gè)字節(jié)的。?
英文字母或符號(hào)只占用一個(gè)字節(jié)。?
Char(10)最多可存放5個(gè)漢字。?
§1.2? varchar2?
是一種變長(zhǎng)的字符類型。最多可占用4000字節(jié)的存儲(chǔ)空間。?
1. 創(chuàng)建一個(gè)表,只有一列,類型為varchar2,長(zhǎng)度為10?
SQL> create table test_varchar( col varchar2(10));?
Table created?
2. 插入一些數(shù)據(jù)?
SQL> insert into test_varchar values('a');?
1 row inserted?
SQL> insert into test_varchar values('aa');?
1 row inserted?
SQL> insert into test_varchar values('aaa');?
1 row inserted?
SQL> insert into test_varchar values('aaaaaaaaaa');?
1 row inserted?
SQL> insert into test_varchar values('aaaaaaaaaaa');?
2. 用dump函數(shù)查看每一行的內(nèi)部存儲(chǔ)結(jié)構(gòu)。?
SQL> select col, dump(col) from test_varchar;?
COL??????? DUMP(COL)?
---------- --------------------------------------------------------------------------------?
a????????? Typ=1 Len=1: 97?
aa???????? Typ=1 Len=2: 97,97?
aaa??????? Typ=1 Len=3: 97,97,97?
aaaaaaaaaa Typ=1 Len=10: 97,97,97,97,97,97,97,97,97,97?
Typ=1,說明varchar2類型在oracle中的類型編號(hào)為1?
Len代表了每一行數(shù)據(jù)所占用的字節(jié)數(shù)。?
后面是具體的存儲(chǔ)值。?
由此可見,varchar2是存多少就占用多少空間。比較節(jié)省空間的。不會(huì)像char那樣用空格填充。?
§1.3? byte 和char?
在10g中,字符類型的寬度定義時(shí),可以指定單位。?
Byte就是字節(jié)。?
Char就是字符。?
Varchar2(10 byte) 長(zhǎng)度為10個(gè)字節(jié)。?
Varchar2(10 char) 長(zhǎng)度為10個(gè)字符所占的長(zhǎng)度。?
Char(10 byte)長(zhǎng)度為10個(gè)字節(jié)。?
Char(10 char) 長(zhǎng)度為10個(gè)字符所占的長(zhǎng)度。?
一個(gè)字符占用多少個(gè)字節(jié),是由當(dāng)前系統(tǒng)采用的字符集來決定的。?
如一個(gè)漢字占用兩個(gè)字節(jié)。?
查看當(dāng)前系統(tǒng)采用的字符集?
SQL> select * from nls_database_parameters where parameter ='NLS_CHARACTERSET';?
PARAMETER????????????????????? VALUE?
------------------------------ --------------------------------------------------------------------------------?
NLS_CHARACTERSET?????????????? ZHS16GBK?
如果在定義類型時(shí),不指定單位。默認(rèn)是按byte,即以字節(jié)為單位的。?
采用char為單位的好處是,使用多字節(jié)的字符集。?
比如,在ZHS16GBK字符集中,一個(gè)漢字占用兩個(gè)字節(jié)。?
把數(shù)據(jù)表的某一列長(zhǎng)度定義為可存放10個(gè)漢字,通過下面的定義就可以了。?
Create table test_varchar(col_char? varchar2(10 char));?
這樣相對(duì)簡(jiǎn)單一些。在數(shù)據(jù)庫(kù)表設(shè)計(jì)時(shí)需要注意。?
繼續(xù)實(shí)驗(yàn),新建一個(gè)表,包含兩列。一列采用byte為單位,一列采用char為單位?
SQL> create table test_varchar2 (col_char varchar2(10 char),col_byte varchar2(10 byte));?
Table created?
Col_char列,定義為可存放10個(gè)字符。?
Col_byte 列,定義為可存放10個(gè)字節(jié)的字符。?
當(dāng)前的系統(tǒng)采用字符集為ZHS16GBK.所以一個(gè)字符占兩個(gè)字節(jié)。?
試著在表中插入一些數(shù)據(jù)?
SQL> insert into test_varchar2 values('a','a');?
1 row inserted?
SQL> insert into test_varchar2 values('袁','a');?
1 row inserted?
SQL> insert into test_varchar2 values('袁袁袁袁袁袁袁袁袁袁','aaaaaaaaaa');?
1 row inserted?
SQL> insert into test_varchar2 values('袁袁袁袁袁袁袁袁袁袁','袁袁袁袁袁袁袁袁袁袁');?
insert into test_varchar2 values('袁袁袁袁袁袁袁袁袁袁','袁袁袁袁袁袁袁袁袁袁')?
ORA-12899: value too large for column "PUB_TEST"."TEST_VARCHAR2"."COL_BYTE" (actual: 20, maximum: 10)?
第一次, 在兩列中都插入字符a?
第二次, 在col_char列插入字符’袁’,在col_byte插入字符a?
第三次, 在col_char列中插入10個(gè)中文字符’袁’,在col_byte插入10個(gè)字符a?
第四次, 在兩列中都插入中文字符’袁’時(shí),報(bào)錯(cuò)了。第二列長(zhǎng)度不夠。?
?? 再看看每一行的存儲(chǔ)結(jié)構(gòu)?
SQL> select col_char, dump(col_char) from test_varchar2;?
COL_CHAR???????????? DUMP(COL_CHAR)?
-------------------- --------------------------------------------------------------------------------?
a??????????????????? Typ=1 Len=1: 97?
袁?????????????????? Typ=1 Len=2: 212,172?
袁袁袁袁袁袁袁袁袁袁 Typ=1 Len=20: 212,172,212,172,212,172,212,172,212,172,212,172,212,172,212,172,21?
當(dāng)我們?cè)赾ol_char列插入10個(gè)漢字時(shí),它的長(zhǎng)度為20.?
盡管我們?cè)诙x的時(shí)候是采用varchar2(10,char).?
由此可見,oracle是根據(jù)當(dāng)前數(shù)據(jù)庫(kù)采用的字符集,每個(gè)字符的所占字節(jié)數(shù) X 字段長(zhǎng)度來決定了該字段所占的字節(jié)數(shù)。?
在本例中,varchar2(10,char)相當(dāng)于varchar2(20).?
不信,我們可以試試看。?
SQL> desc test_varchar2;?
Name???? Type???????? Nullable Default Comments?
-------- ------------ -------- ------- --------?
COL_CHAR VARCHAR2(20) Y?????????????????????????
COL_BYTE VARCHAR2(10) Y??
當(dāng)采用多字節(jié)的字符集時(shí),定義字段長(zhǎng)度還是采用char為單位指定為佳。因?yàn)榭梢员苊庾侄伍L(zhǎng)度的問題。?
當(dāng)不知道當(dāng)前數(shù)據(jù)庫(kù)采用的字符集,一個(gè)字符占用多少字節(jié)時(shí),可以使用lengthb函數(shù)。?
SQL> select lengthb('袁') from dual;?
LENGTHB('袁')?
-------------?
??????????? 2?
§1.4? char還是varchar?
1. 新建一個(gè)表,一列為char類型,一列為varchar2類型?
SQL> create table test_char_varchar(char_col char(20),varchar_col varchar2(20));?
Table created?
2. 向該表中的兩列都插入相關(guān)的數(shù)據(jù)?
SQL> insert into test_char_varchar values('Hello World','Hello World');?
1 row inserted?
SQL> select * from test_char_varchar;?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
Hello World????????? Hello World?
3. 以char_col列為條件查詢?
SQL> select * from test_char_varchar where char_col ='Hello World';?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
Hello World????????? Hello World?
4. 以varchar_col列為條件查詢?
SQL> select * from test_char_varchar where varchar_col ='Hello World';?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
Hello World????????? Hello World?
5.似乎char 和varchar類型沒有什么兩樣。再看看下面的語(yǔ)句。?
SQL> select * from test_char_varchar where varchar_col =char_col;?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
這已經(jīng)看出他們并不一樣,這涉及到字符串比較的問題。?
因?yàn)橐呀?jīng)發(fā)生了隱式轉(zhuǎn)換,在與char列char_col進(jìn)行比較時(shí),char_col列的內(nèi)容已經(jīng)轉(zhuǎn)換成了char(20).在Hello World后面以空格進(jìn)行填充了。而varchar_col列并沒有發(fā)生這種轉(zhuǎn)換。?
如果要讓char_col列與varchar_col列相等。有兩種方法。?
第一種是:使用trim把char_col列的空格去掉。?
第二種是:使遙rpad把varchar_col列用空格進(jìn)行填充長(zhǎng)度為20的字符。?
SQL> select * from test_char_varchar where trim(char_col) = varchar_col;?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
Hello World????????? Hello World?
SQL> select * from test_char_varchar where char_col = rpad(varchar_col,20);?
CHAR_COL???????????? VARCHAR_COL?
-------------------- --------------------?
Hello World????????? Hello World?
如果使用trim函數(shù),如果char_col列上有索引,那么索引將不可用了。?
此外還會(huì)在綁定變量時(shí)出現(xiàn)問題。?
§1.5? NCHAR和NVARCHAR2?
如果系統(tǒng)需要集中管理和存儲(chǔ)多種字符集,就需要使用這兩種字符類型。在使用NCAHR和NVARCHAR2時(shí),文本內(nèi)容采用國(guó)家字符集來存儲(chǔ)和管理。而不是默認(rèn)字符集。?
這兩種類型的長(zhǎng)度指的是字符數(shù),而不是字節(jié)數(shù)。?
NLS國(guó)家語(yǔ)言支持(National Language Support)?
在oracle 9i及以后的版本,數(shù)據(jù)庫(kù)的國(guó)家字符集可以是:utf-8和AL16UTF-16兩種。?
Oracle 9i是utf -8, Oralce 10g是AL16UTF-16.?
1.新建一個(gè)表,有兩列,類型分別為:nchar和nvarchar2.長(zhǎng)度都為10?
SQL> create table test_nvarchar(col_nchar nchar(10),col_nvarchar2 nvarchar2(10));?
Table created?
2.插入一些數(shù)據(jù)?
SQL> insert into test_nvarchar values('袁','袁光東');?
1 row inserted?
SQL> insert into test_nvarchar values(N'袁',N'袁光東');?
1 row inserted?
(在9i之前的版本,插入時(shí)加上N時(shí),在處理時(shí)跟普通方式有不同的方式。但是在10g的時(shí)候已經(jīng)有了改變,加不加N都是一樣,這里只是為了測(cè)試)?
SQL> insert into test_nvarchar values('a','b');?
1 row inserted?
插入一行英文字母?
3. 查看每行的col_nchar列的存儲(chǔ)方式。?
SQL> select col_nchar, dump(col_nchar) from test_nvarchar;?
COL_NCHAR??????????? DUMP(COL_NCHAR)?
-------------------- --------------------------------------------------------------------------------?
袁?????????????????? Typ=96 Len=20: 136,129,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32?
a??????????????????? Typ=96 Len=20: 0,97,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32?
袁?????????????????? Typ=96 Len=20: 136,129,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32?
Typ=96 與char的類型編碼一樣。?
Len=20 每一行的長(zhǎng)度都是20字節(jié)。這一點(diǎn)跟char一樣。都是定長(zhǎng)的,會(huì)以空格填充。?
需要注意的是:統(tǒng)統(tǒng)以兩位來表示一個(gè)字符。?
136,129 表示’袁’?
0,97 表示’a’?
0,32 表示空格。?
4. nvarchar2的儲(chǔ)存?
SQL> select col_nvarchar2, dump(col_nvarchar2) from test_nvarchar;?
COL_NVARCHAR2??????? DUMP(COL_NVARCHAR2)?
-------------------- --------------------------------------------------------------------------------?
袁光東?????????????? Typ=1 Len=6: 136,129,81,73,78,28?
b??????????????????? Typ=1 Len=2: 0,98?
袁光東?????????????? Typ=1 Len=6: 136,129,81,73,78,28?
Typ=1 與varchar2一樣。?
每一行的len值都不樣同。不會(huì)使用空格進(jìn)行填充。?
每一個(gè)字符都占有兩個(gè)字節(jié)兩進(jìn)行存儲(chǔ)。?
b 存儲(chǔ)為: 0, 98?
袁 存儲(chǔ)為: 136,129?
5.nchar和nvarchar2的數(shù)據(jù)定義。?
SQL> desc test_nvarchar;?
Name????????? Type????????? Nullable Default Comments?
------------- ------------- -------- ------- --------?
COL_NCHAR???? NCHAR(20)???? Y?????????????????????????
COL_NVARCHAR2 NVARCHAR2(20) Y????
雖然在定義nchar和nvarchar2時(shí),指定的長(zhǎng)度是指字符數(shù)。但是表結(jié)構(gòu)的定義中,仍然是存儲(chǔ)著它的字節(jié)數(shù)。?
在定義時(shí)nchar(10)表示可以最大存儲(chǔ)10個(gè)字符。?
在查看數(shù)據(jù)表結(jié)構(gòu)時(shí),顯示該列最大占用的字節(jié)數(shù)。?
需要注意的是:在char和nchar中對(duì)漢字的實(shí)際存儲(chǔ)值是不一樣的。因?yàn)椴捎昧瞬煌淖址?#xff0c;就有了不同的字符編碼。?
SQL> insert into test_varchar values('袁');?
1 row inserted?
SQL> select col, dump(col) from test_varchar where col='袁';?
COL??????? DUMP(COL)?
---------- --------------------------------------------------------------------------------?
袁???????? Typ=1 Len=2: 212,172?
這時(shí)采用的字符集系統(tǒng)默認(rèn)字符集ZHS16GBK。?
這里很容易的把它轉(zhuǎn)換成ascii碼。?
高位 * 256(2的8次方) + 低位.?
212 * 256 + 172 = 54444?
SQL> select chr(54444) from dual;?
CHR(54444)?
----------?
袁?
而在Nchar 和Nvarchar中,采用的是UTF-8或UTF-16的字符集。?
SQL> insert into test_nvarchar values('袁','袁');?
1 row inserted?
SQL> select col_nvarchar2, dump(col_nvarchar2) from test_nvarchar where col_nvarchar2='袁';?
COL_NVARCHAR2??????? DUMP(COL_NVARCHAR2)?
-------------------- --------------------------------------------------------------------------------?
袁?????????????????? Typ=1 Len=2: 136,129?
‘袁’存儲(chǔ)的值為:136,129?
Oracle 10以上對(duì)nchar和nvarchar都采用utf-16字符集了。它的好處就是對(duì)字符采用固定長(zhǎng)度的字節(jié)存儲(chǔ)(2字節(jié)),支持多國(guó)字符,在操作效率上會(huì)更高。但是它卻無法兼容于ascii碼。?
§1.6? RAW?
RAW與CHAR和VARCHAR2相比。RAW屬于二進(jìn)制數(shù)據(jù),更可以把它稱為二進(jìn)制串。在對(duì)CHAR和VARCHAR2類型進(jìn)行存儲(chǔ)時(shí),會(huì)進(jìn)行字符集轉(zhuǎn)換。而對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行存儲(chǔ)則不會(huì)進(jìn)行字符集轉(zhuǎn)換。?
SQL> create table test_raw (col_chr varchar2(10), col_raw raw(10));?
Table created?
SQL> insert into test_raw values('aa','aa');?
1 row inserted?
SQL> commit;?
Commit complete?
SQL> select * from test_raw;?
COL_CHR??? COL_RAW?
---------- --------------------?
aa???????? AA?
SQL> select col_chr,dump(col_chr) from test_raw;?
COL_CHR??? DUMP(COL_CHR)?
---------- --------------------------------------------------------------------------------?
aa???????? Typ=1 Len=2: 97,97?
SQL> select col_raw,dump(col_raw) from test_raw;?
COL_RAW????????????? DUMP(COL_RAW)?
-------------------- --------------------------------------------------------------------------------?
AA?????????????????? Typ=23 Len=1: 170?
通過上面的分析,雖然我們通過select查詢得到的結(jié)果,raw列顯示為插入的字符。但是我們通過dump函數(shù)得知到raw并不是以字符的方式存儲(chǔ)。它是把插入的字符認(rèn)為是16進(jìn)制的值。?
比如本例,我們向raw列插入aa,但是它占用的空間為1個(gè)字節(jié)。值為170.?
170轉(zhuǎn)為16進(jìn)制正好是aa?
向raw列插入數(shù)據(jù)時(shí)會(huì)發(fā)生一個(gè)隱式轉(zhuǎn)換HEXTORAW?
從raw列讀取數(shù)據(jù)時(shí)會(huì)發(fā)生一個(gè)隱式轉(zhuǎn)換RAWTOHEX?
如果向raw列插入值不是有效的十六進(jìn)制值時(shí),會(huì)報(bào)錯(cuò)的。?
SQL> insert into test_raw values('h','h');?
insert into test_raw values('h','h')?
ORA-01465: invalid hex number?
第二部分 數(shù)值類型?
§ 2.1? number?
Number類型是oralce的數(shù)值類型,存儲(chǔ)的數(shù)值的精度可以達(dá)到38位。Number是一種變長(zhǎng)類型,長(zhǎng)度為0-22字節(jié)。取值范圍為:10e-130 – 10e 126(不包括)?
Number(p,s)?
P和s都是可選的。?
P指精度(precision),即總位數(shù)。默認(rèn)情況下精度為38。精度的取值范圍為1~38.?
S指小數(shù)位(scale).小數(shù)點(diǎn)右邊的位數(shù)。小數(shù)點(diǎn)位數(shù)的合法值為-48~127。小數(shù)位的默認(rèn)值由精度來決定。如果沒有指定精度,小數(shù)位默認(rèn)為最大的取值區(qū)間.如果指定了精度,沒有指定小數(shù)位。小數(shù)位默認(rèn)為0(即沒有小數(shù)位).?
精度和小數(shù)位不會(huì)影響數(shù)據(jù)如何存儲(chǔ),只會(huì)影響允許哪些數(shù)值及數(shù)值如何舍入。?
1.新建一個(gè)表?
SQL> create table test_number(col_number number(6,2));?
Table created?
2.插入一些不同的數(shù)據(jù)?
SQL> insert into test_number values(-1);?
1 row inserted?
SQL> insert into test_number values(0);?
1 row inserted?
SQL> insert into test_number values(1);?
1 row inserted?
SQL> insert into test_number values(2);?
1 row inserted?
SQL> insert into test_number values(11.00);?
1 row inserted?
SQL> insert into test_number values(11.11);?
1 row inserted?
SQL> insert into test_number values(1234.12);?
1 row inserted?
SQL> insert into test_number values(-0.1);?
1 row inserted?
SQL> insert into test_number values(-11.11);?
1 row inserted?
SQL> insert into test_number values(-1234.12);?
1 row inserted?
SQL> commit;?
Commit complete?
3.查看結(jié)果?
SQL> select * from test_number;?
COL_NUMBER?
----------?
???? -1.00?
????? 0.00?
????? 1.00?
????? 2.00?
???? 11.00?
???? 11.11?
?? 1234.12?
???? -0.10?
??? -11.11?
? -1234.12?
10 rows selected?
5. 查看存儲(chǔ)結(jié)構(gòu)?
SQL> select col_number, dump(col_number) from test_number;?
COL_NUMBER DUMP(COL_NUMBER)?
---------- --------------------------------------------------------------------------------?
???? -1.00 Typ=2 Len=3: 62,100,102?
????? 0.00 Typ=2 Len=1: 128?
????? 1.00 Typ=2 Len=2: 193,2?
????? 2.00 Typ=2 Len=2: 193,3?
???? 11.00 Typ=2 Len=2: 193,12?
???? 11.11 Typ=2 Len=3: 193,12,12?
?? 1234.12 Typ=2 Len=4: 194,13,35,13?
???? -0.10 Typ=2 Len=3: 63,91,102?
??? -11.11 Typ=2 Len=4: 62,90,90,102?
? -1234.12 Typ=2 Len=5: 61,89,67,89,102?
10 rows selected?
由此可見:?
Number類型的內(nèi)部編碼為:2?
根據(jù)每一行的len值可以看出,number是一個(gè)變長(zhǎng)類型。不同的數(shù)值占用不同的空間。?
如果指定了精度,顯示結(jié)果與精度相關(guān)。?
就像我插入語(yǔ)句寫為?
insert into test_number values(0);?
但是顯示結(jié)果為:0.00?
如果數(shù)值是負(fù)數(shù),在最后一位上填充一個(gè)補(bǔ)碼102.即表示該數(shù)值為負(fù)數(shù)。?
0是一個(gè)特殊的值,它在oracle中存儲(chǔ)為128.?
第一位為標(biāo)志位。以128為比較。如果數(shù)值大于128,則它大于0。如果小于128小于0。?
-1的內(nèi)部存儲(chǔ)為:?
-1.00 Typ=2 Len=3: 62,100,102?
最后一位是102,是一個(gè)負(fù)數(shù)。?
第一位小于128,所以小于10.?
除了第一位標(biāo)志位外,其它的都是數(shù)值為了。?
如果該值是一個(gè)正數(shù)。每一位的存儲(chǔ)值減1為每一位的實(shí)際值。?
1.0的存儲(chǔ)結(jié)構(gòu)為:?
1.00 typ=2 Len=2: 193,2?
實(shí)值上1.00的存儲(chǔ)結(jié)果與1相同。?
第一位193為標(biāo)志位,大于128,大于0.?
第二位為數(shù)值為,因?yàn)槭钦龜?shù),實(shí)際值為存儲(chǔ)值減1。2-1 = 1。?
如是該值是一個(gè)負(fù)數(shù),每一位的實(shí)際值為101 減去存儲(chǔ)的值。?
-1.00的存儲(chǔ)結(jié)構(gòu)為:?
-1.00 Typ=2 Len=3: 62,100,102?
最后一位102為補(bǔ)位。?
第一位62為標(biāo)志位,小于128。實(shí)際值小于0.?
第二位為數(shù)值為,因?yàn)槭秦?fù)數(shù)。實(shí)際值為:101 – 100? =1.?
§2.2 小數(shù)位在哪里??
從上面的存儲(chǔ)結(jié)果看,對(duì)小數(shù)存儲(chǔ)時(shí),它并沒有一個(gè)小數(shù)的標(biāo)志位。但是它實(shí)際上是由第一位標(biāo)志位,和數(shù)值位(第二位)來決定的。?
當(dāng)存儲(chǔ)的數(shù)是一個(gè)正數(shù),該數(shù)值的前幾位為:第一位 * power(100 , (標(biāo)志位 - 193));?
當(dāng)存儲(chǔ)的數(shù)是一個(gè)負(fù)數(shù),該數(shù)值的前幾位為:第一位 * power(100,(62 – 標(biāo)志位));?
11.11的存儲(chǔ)結(jié)果為:?
11.11 Typ=2 Len=3: 193,12,12?
第一位數(shù)值位為:12 實(shí)際數(shù)值為11?
標(biāo)志位為:193?
12 * power(100, (193- 193);?
?? 100的零次方為1.?
12 乘1 等于12.?
所以這個(gè)數(shù)的前幾位為:12。從這后面就是小數(shù)了。?
1234.12的存儲(chǔ)結(jié)構(gòu)為:?
1234.12 Typ=2 Len=4: 194,13,35,13?
第一位數(shù)值位為:13,實(shí)際值為12?
標(biāo)志位為:193?
13 * power(100,(194-193)) = 1300?
所以前四位為整數(shù)位,后面的為小數(shù)位。?
-0.10的存儲(chǔ)結(jié)構(gòu)為:?
-0.10 Typ=2 Len=3: 63,91,102?
標(biāo)志位為:63?
第一位數(shù)值為:91 ,實(shí)際值為:10?
91 * (100,(62-63)) =-9100.?
所以小數(shù)位在91之前。?
-1234.12的存儲(chǔ)結(jié)構(gòu)為:?
-1234.12 Typ=2 Len=5: 61,89,67,89,102?
標(biāo)志位為:61?
第一位數(shù)值為:89?
89*(100,(62-61)) =8900?
所以小數(shù)位在67之后。?
§2.3 number的精度和小數(shù)位?
Number類型的精度最多可是38位。小數(shù)位-84--127位。?
SQL> create table test_number1(col_number number(39));?
create table test_number1(col_number number(39))?
ORA-01727: numeric precision specifier is out of range (1 to 38)?
指定小數(shù)位時(shí),精度只能是1-38。不能是0?
SQL> create table test_number1(col_number number(0,127));?
create table test_number1(col_number number(0,127))?
ORA-01727: numeric precision specifier is out of range (1 to 38)?
SQL> create table test_number1(col_number number(1,128));?
create table test_number1(col_number number(1,128))?
ORA-01728: numeric scale specifier is out of range (-84 to 127)?
精度與小數(shù)位的關(guān)系。精度并不是小數(shù)位加整數(shù)位之和。?
我們先看看小數(shù)位為0的情況。?
SQL> create table test_number1(col_char varchar2(200), col_num number(10));?
Table created?
Number(10).只定義了精度,小數(shù)位為0.?
看看它可以存放的數(shù)據(jù)。?
SQL> insert into test_number1 values('9999999999',9999999999);?
1 row inserted?
插入了10個(gè)9,沒有問題,再插入多一位看看?
SQL> insert into test_number1 values('99999999991',99999999991);?
insert into test_number1 values('99999999991',99999999991)?
ORA-01438: value larger than specified precision allowed for this column?
報(bào)錯(cuò)了,精度不夠。?
再看看能不能再插入小數(shù)??
SQL> insert into test_number1 values('0.9',0.9);?
1 row inserted?
SQL> select * from test_number1;?
Col_char COL_NUM?
-------------------- --------------?
9999999999 9999999999?
0.9 ???????????? 1?
注意插入數(shù)值0.9后,存儲(chǔ)為1.這就是小數(shù)位的作用。在哪里進(jìn)行舍入。?
帶小數(shù)位和精度的情況。?
SQL> create table test_number2(col_char varchar(20),col_num number(1,3));?
Table created?
精度是1,小數(shù)位是3.?
可見,精度不是小數(shù)位加整數(shù)位了。但是精度和小數(shù)位倒底什么關(guān)系呢??
SQL> insert into test_number2 values('0.111',0.111);?
insert into test_number2 values('0.111',0.111)?
ORA-01438: value larger than specified precision allowed for this column?
插入3位小數(shù),0.111竟然報(bào)錯(cuò)了,說精度不夠。?
SQL> insert into test_number2 values('0.001',0.001);?
1 row inserted?
插入0.001時(shí),成功了。?
SQL> insert into test_number2 values('0.001',0.0015);?
1 row inserted?
插入0.0015也成功了。?
看看插入的值。?
SQL> select * from test_number2;?
COL_CHAR???????????? COL_NUM?
-------------------- -------?
0.001????????????????? 0.001?
0.0015???????????????? 0.002?
需要注意的是0.0015被舍入為0.002?
精度大于小數(shù)位?
SQL> create table test_number3 (col_char varchar(20), col_number number(5,3));?
Table created?
SQL> insert into test_number3 values('99.899',99.899);?
1 row inserted?
SQL> insert into test_number3 values('99.999',99.999);?
1 row inserted?
SQL> insert into test_number3 values('99.9999',99.9999);?
insert into test_number3 values('99.9999',99.9999)?
ORA-01438: value larger than specified precision allowed for this column?
注意,當(dāng)插入99.9999時(shí),系統(tǒng)報(bào)錯(cuò)。因?yàn)樾?shù)位為3位。第四位小數(shù)位是9,于是往前入。最終變成100.000.就已經(jīng)超過了精度。?
Number(5,3)可存儲(chǔ)的數(shù)值最大為99.999.?
現(xiàn)在終于有點(diǎn)明白小數(shù)位與精度的關(guān)系了。?
number(38,127)?
可以存儲(chǔ)的最大小數(shù)為:127位小數(shù),最后38為9.?
即:0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099999999999999999999999999999999999999
小數(shù)位為負(fù)數(shù)。?
我們從前面知道,小數(shù)位的取值為-48 ~127?
為什么小數(shù)位會(huì)為負(fù)數(shù)?這有點(diǎn)怪異了。像上面的number(5,3)將值舍入為最接近0.001?
Number(5,-3)就是將值舍入為最接近的1000?
SQL> create table test_number5 (col_char varchar(20), col_num number(5,-3));?
Table created?
插入值10999?
SQL> insert into test_number5 values('10999',10999);?
1 row inserted?
查看一下結(jié)果?
SQL> select * from test_number5;?
COL_CHAR???????????? COL_NUM?
-------------------- -------?
10999????????????????? 11000?
存儲(chǔ)的結(jié)果為:11000?
當(dāng)小數(shù)部分為負(fù)數(shù)時(shí),是對(duì)小數(shù)部分進(jìn)行舍入。?
那么精度在這時(shí)起到什么作用呢?與小數(shù)位又有什么關(guān)系??
SQL> insert into test_number5 values('111111111',111111111);?
insert into test_number5 values('111111111',111111111)?
ORA-01438: value larger than specified precision allowed for this column?
插入9個(gè)1時(shí),報(bào)錯(cuò)精度不夠。?
SQL> insert into test_number5 values('11111111',11111111);?
1 row inserted?
插入8個(gè)1時(shí),正確插入。?
我們看看它的結(jié)果,看它是怎么舍入的。?
SQL> select * from test_number5;?
COL_CHAR???????????? COL_NUM?
-------------------- -------?
11111111???????????? 11111000?
結(jié)果是1111100而不是1111100?
無限接近1000,就是從百位開始進(jìn)行四舍五入,后面的值全部為0。?
所以看出number(5,-3)可存儲(chǔ)的最大值為:99999000?
SQL> insert into test_number5 values('99999499.999999',99999499.999999);?
1 row inserted?
SQL> select * from test_number5;?
COL_CHAR???????????? COL_NUM?
-------------------- -------?
99999999???????????? 99999000?
99999499.999999????? 99999000?
現(xiàn)在應(yīng)該明白了精度和小數(shù)位的關(guān)系了吧。?
小數(shù)位告訴系統(tǒng)保留多少位小數(shù),從哪里開始舍入。?
精度舍入后,從舍入的位置開始,數(shù)值中允許有多少位。?
§2.4? binary_float 和binary_double?
這兩種類型是oracle 10g新引進(jìn)的數(shù)值類型。在oracle 10g之前是沒有這兩種類型的。?
Number類型是由oracle軟件支持的類型。而浮點(diǎn)數(shù)用于近似數(shù)值。但是它浮點(diǎn)數(shù)允許由在硬盤上(CPU,芯片)上執(zhí)行運(yùn)行。而不是在oracel進(jìn)程中運(yùn)算。如果希望在一個(gè)科學(xué)計(jì)算中執(zhí)行實(shí)數(shù)處理,依賴于硬件的算術(shù)運(yùn)算速度要快得多。但是它的精度卻很小。如果希望用來存儲(chǔ)金融數(shù)值,則必須用number.?
BINARY_FLOAT是一種IEEE固有的單精度浮點(diǎn)數(shù)。可存儲(chǔ)6位精度,取值范圍在~±1038.25的數(shù)值。?
BINARY_DOUBLE是一種IEEE固有的雙精度浮點(diǎn)數(shù)。可存儲(chǔ)12位精度。取值范圍在~±10308.25的數(shù)值?
SQL> create table test_floatdouble(col_number number, col_float binary_float, col_double binary_double);?
Table created?
SQL> insert into test_floatdouble values(9876543210.0123456789,9876543210.0123456789,9876543210.0123456789);?
1 row inserted?
2 SQL> select to_char(col_number), to_char(col_float), to_char(col_double) from test_floatdouble;?
3
4 TO_CHAR(COL_NUMBER)????????????????????? TO_CHAR(COL_FLOAT)?????????????????????? TO_CHAR(COL_DOUBLE)?
5 ---------------------------------------- ---------------------------------------- ----------------------------------------?
6 9876543210.0123456789??????????????????? 9.87654349E+009????????????????????????? 9.8765432100123463E+009?
由此可見,binary_float無法表示這個(gè)數(shù)。Binary_float和binary_double無法用于對(duì)精度要求高的數(shù)據(jù)。?
SQL> select dump(col_float)from test_floatdouble;?
DUMP(COL_FLOAT)?
--------------------------------------------------------------------------------?
Typ=100 Len=4: 208,19,44,6?
BINARY_FLOAT 類型編碼為100?
Len=4 占用4個(gè)字節(jié)。它是采用固定字節(jié)進(jìn)行存儲(chǔ)的。?
SQL> select dump(col_double)from test_floatdouble;?
DUMP(COL_DOUBLE)?
--------------------------------------------------------------------------------?
Typ=101 Len=8: 194,2,101,128,183,80,25,73?
BINARY_DOUBLE 類型編碼為101?
Leng= 8 占用8個(gè)字節(jié)。也是采用固定字節(jié)進(jìn)行存儲(chǔ)。?
注意:number 類型使用的CPU時(shí)間是浮點(diǎn)數(shù)類型的50倍。浮點(diǎn)數(shù)是數(shù)值的一個(gè)近似值,精度在6-12位之間。從Number類型得到的結(jié)果要比從浮點(diǎn)數(shù)得到的結(jié)果更精確。但在對(duì)科學(xué)數(shù)據(jù)進(jìn)行數(shù)據(jù)挖掘和進(jìn)行復(fù)雜數(shù)值分析時(shí),精度的損失是可以接受的,還會(huì)帶來顯著的性能提升。?
這時(shí)需要使用內(nèi)置CAST函數(shù),對(duì)NUMBER類型執(zhí)行一種實(shí)時(shí)的轉(zhuǎn)換,在執(zhí)行復(fù)雜數(shù)學(xué)運(yùn)算之前先將其轉(zhuǎn)換為一種浮點(diǎn)數(shù)類型。CPU使用時(shí)間就與固有浮點(diǎn)類型使用的CPU時(shí)間非常接近了。?
Select ln(cast(number_col as binary_double)) from test_number.?
§2.5 Oracle在語(yǔ)法上還支持的數(shù)值數(shù)據(jù)類型?
NUMERIC(p,s):完全映射到NUMBER(p,s)。如果p未指定,則默認(rèn)為38.?
DECIMAL(p,s)或DEC(p,s):同NUMERIC(p,s).?
INTEGER或int:完全映射至NUMBER(38)?
SMALLINT:完全映射至NUMBER(38)?
FLOAT(b):映射至NUMBER?
DOUBLE PRECISION:映射到NUMBER?
REAL:映射到NUMBER.?
第三部分 日期時(shí)間類型?
§3.1? DATE?
Date類型Oralce用于表示日期和時(shí)間的數(shù)據(jù)類型。固定占用7個(gè)字節(jié)。?
包括七個(gè)屬性:?
世紀(jì)?
世紀(jì)中的年份?
月份?
月份中的哪一天?
小時(shí)?
分?
秒?
SQL> create table test_date(col_date date);?
Table created?
SQL> insert into test_date values(to_date('2008-06-27 10:35:00','yyyy-mm-dd hh24:mi:ss'));?
1 row inserted?
SQL> select to_char(col_date,'yyyy-mm-dd hh24:mi:ss'),dump(col_date) from test_date;?
TO_CHAR(COL_DATE,'YYYY-MM-DDHH DUMP(COL_DATE)?
------------------------------ --------------------------------------------------------------------------------?
2008-06-27 10:35:00??????????? Typ=12 Len=7: 120,108,6,27,11,36,1?
Date類型的內(nèi)部編碼為12?
長(zhǎng)度:占用7個(gè)字節(jié)?
數(shù)據(jù)存儲(chǔ)的每一位到第七位分別為:世紀(jì),年,月,日,時(shí),分,秒。?
世紀(jì):采用”加100”表示法來存儲(chǔ)。即世紀(jì)+100來存儲(chǔ)。120 – 100 = 20?
年:跟世紀(jì)一樣采用”加100”表示法來存儲(chǔ)。108 – 100 = 08(采用兩位表示)?
月:自然存儲(chǔ).6?
日:自然存儲(chǔ),不做修改,27?
時(shí):(時(shí),分,秒都采用“加1”法存儲(chǔ))11 -1= 10?
分:36 -1 = 35?
秒:1 -1 = 0?
為什么世紀(jì)和年份要用加100法存儲(chǔ)呢?是為了支持BC和AD日期。?
BC即為公元前。?
AD即為公元。?
如果世紀(jì) – 100為一個(gè)負(fù)數(shù),那么就是一個(gè)BC日期。?
插入一個(gè)公元前日期?
SQL> insert into test_date values(to_date('-4712-01-01','syyyy-mm-dd hh24:mi:ss'));?
1 row inserted?
SQL> select to_char(col_date,'bc yyyy-mm-dd hh24:mi:ss'),dump(col_date) from test_date;?
TO_CHAR(COL_DATE,'BCYYYY-MM-DD DUMP(COL_DATE)?
------------------------------ --------------------------------------------------------------------------------?
公元 2008-06-27 10:35:00?????? Typ=12 Len=7: 120,108,6,27,11,36,1?
公元前 4712-01-01 00:00:00???? Typ=12 Len=7: 53,88,1,1,1,1,1?
我們已經(jīng)了解了日期的存儲(chǔ)結(jié)構(gòu)。當(dāng)要對(duì)日期進(jìn)行截取時(shí),比如去掉時(shí),分,秒。只需要把最后的三個(gè)字節(jié)設(shè)為:12 12 1就可以了。?
SQL> create table test_date1 (col_char varchar2(12), col_date date);?
Table created?
SQL> insert into test_date1 values('full',to_date('2008-06-27 12:01:00','yyyy-mm-dd hh24:mi:ss'));?
1 row inserted?
SQL> insert into test_date1(col_char,col_date) select 'minute', trunc(col_date,'mi') from test_date1?
? 2???? union all?
? 3????? select 'day', trunc(col_date,'dd') from test_date1?
? 4???? union all?
? 5????? select 'month',trunc(col_date,'mm') from test_date1?
? 6????? union all?
? 7?????? select 'year',trunc(col_date,'y') from test_date1?
? 8? ;?
4 rows inserted?
SQL> select col_char, col_date,dump(col_date) from test_date1;?
COL_CHAR???? COL_DATE??? DUMP(COL_DATE)?
------------ ----------- --------------------------------------------------------------------------------?
full???????? 2008-6-27 1 Typ=12 Len=7: 120,108,6,27,13,2,1?
minute?????? 2008-6-27 1 Typ=12 Len=7: 120,108,6,27,13,2,1?
day????????? 2008-6-27?? Typ=12 Len=7: 120,108,6,27,1,1,1?
month??????? 2008-6-1??? Typ=12 Len=7: 120,108,6,1,1,1,1?
year???????? 2008-1-1??? Typ=12 Len=7: 120,108,1,1,1,1,1?
要把一個(gè)日期截取,只取到年。數(shù)據(jù)庫(kù)只是把最后5個(gè)字節(jié)置上1。這是非常快的。?
當(dāng)我們對(duì)一個(gè)Date字段進(jìn)行操作,需要截取到年份進(jìn)行比較時(shí),我們經(jīng)常使用to_char函數(shù)。通過會(huì)這樣寫。?
Select * from test_date1 where to_char(col_date ,’yyyy’) = ‘2008’?
而不是?
Select * from test_date1 where trunc(col_date,’y’) = to_date(‘2008-01-01’,’yyyy-mm-dd’)?
使用trunc會(huì)占用更少的資源,性能更優(yōu)。?
使用to_char所有的CPU時(shí)間與trunc相差一個(gè)數(shù)量級(jí),差不多10倍。因?yàn)閠o_char必須把日期轉(zhuǎn)換成一個(gè)串,并利用當(dāng)前系統(tǒng)所采用的NLS來完成,然后執(zhí)行一個(gè)串與串的比較。而TRUNC只需要把后5個(gè)字節(jié)設(shè)置為1,然后將兩個(gè)7位的字節(jié)的二進(jìn)行數(shù)進(jìn)行比較就搞定了。所要截取一個(gè)DATE列葉,應(yīng)該避免使用to_char.?
另外,要完全避免對(duì)DATE列應(yīng)用函數(shù)。比如我們要查詢2008年的所有數(shù)據(jù),并且這一列上也有索引,我們希望能夠用上這個(gè)索引。?
SQL> select count(col_date) from test_date1 where col_date >= to_date('2008-01-01','yyyy-mm-dd') and col_date < to_date('2009-01-01','yyyy-mm-dd');?
COUNT(COL_DATE)?
---------------?
????????????? 5?
§3.2 向Date類型增加或減時(shí)間?
怎么向Date類型增加時(shí)間,例如:向Date增加1天,或1小時(shí),或1秒,一月等。?
常有的辦法有幾個(gè)方法:?
a.向date增加一個(gè)NUMBER值。因?yàn)镈ate 加減操作是以天為單位。1秒就是 1/24/60/60。依此類推。?
b.使用INTERVAL類型。后續(xù)會(huì)介紹?
c.使用內(nèi)置函數(shù)add_months增加月。增加月不像增加天那么簡(jiǎn)單,所以需要使用內(nèi)置函數(shù)來處理。?
3.2.1? 增加秒?
SQL> create table test_date2(id varchar2(10), operate_time date);?
Table created?
SQL> insert into test_date2 values('1',sysdate);?
1 row inserted?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-06-27 13:35:35?
SQL> update test_date2 set operate_time = operate_time + 1/24/60/60 where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-06-27 13:35:36?
3.2.2 增加分?
SQL> update test_date2 set operate_time = operate_time + 1/24/60 where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-06-27 13:36:36?
3.2.3 增加小時(shí)?
SQL> update test_date2 set operate_time = operate_time + 1/24 where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-06-27 14:36:36?
3.2.4 增加天?
SQL> update test_date2 set operate_time = operate_time + 1 where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-06-28 14:36:36?
3.2.4 增加周?
SQL> update test_date2 set operate_time = operate_time + 1 * 7 where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-07-05 14:36:36?
3.2.5 增加月?
SQL> update test_date2 set operate_time = add_months(operate_time,1)? where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2008-08-05 14:36:36?
3.2.6 增加年?
SQL> update test_date2 set operate_time = add_months(operate_time,1 * 12)? where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2009-08-05 14:36:36?
另外可以使用一個(gè)非常有用的函數(shù)NUMTODSINTERVAL來新增加小時(shí),分鐘,秒。?
SQL> update test_date2 set operate_time = operate_time + numtodsinterval(1,'second') where id=1;?
1 row updated?
SQL> select id, to_char(operate_time, 'yyyy-mm-dd hh24:mi:ss') from test_date2 where id=1;?
ID???????? TO_CHAR(OPERATE_TIME,'YYYY-MM-?
---------- ------------------------------?
1????????? 2009-08-05 14:36:37?
Numtodsinterval(n, 'second') 獲得秒的時(shí)間間隔?
Numtodsinterval(n, 'minute') 獲得分的時(shí)間間隔?
Numtodsinterval(n, 'month') 獲得月的時(shí)間間隔?
Numtodsinterval(n, 'year') 獲得月的時(shí)間間隔?
增加月份時(shí)要非常的小心,應(yīng)該使用add_months函數(shù)。為什么呢??
比如當(dāng)前日期為2000-2-29日。增加一個(gè)月得到的日期就應(yīng)該是2000-3-31?
如果只是簡(jiǎn)單的加30天或加31天,是無法實(shí)現(xiàn)的。所以必須使用add_months函數(shù),它會(huì)自動(dòng)來處理這種月末問題。對(duì)年份進(jìn)行增加也會(huì)出現(xiàn)類似的問題?
§3.3 TIMESTAMP?
TIMESTAMP是支持小數(shù)秒和時(shí)區(qū)的日期/時(shí)間類型。對(duì)秒的精確度更高。?
3.3.1 TIMESTAM語(yǔ)法?
TIMESTAMP(n)?
N的取值為0~9.表示指定TIMESTAMP中秒的小數(shù)位數(shù)。N為可選。如果n為0,timestamp與date等價(jià)。?
SQL> create table test_timestamp(col_date date, col_timestamp timestamp(0));?
Table created?
SQL> insert into test_timestamp values(sysdate,systimestamp);?
1 row inserted?
SQL> select dump(col_date) from test_timestamp;?
DUMP(COL_DATE)?
--------------------------------------------------------------------------------?
Typ=12 Len=7: 120,108,6,27,17,8,37?
SQL> select dump(col_timestamp) from test_timestamp;?
DUMP(COL_TIMESTAMP)?
--------------------------------------------------------------------------------?
Typ=180 Len=7: 120,108,6,27,17,8,38?
如果指定了保留小數(shù)位數(shù),那情況就大不一樣了。?
SQL> create table test_timestamp1 (col_date date, col_timestamp timestamp(9));?
Table created?
SQL> insert into test_timestamp1 values(sysdate, systimestamp);?
1 row inserted?
SQL> select dump(col_date) from test_timestamp1;?
DUMP(COL_DATE)?
--------------------------------------------------------------------------------?
Typ=12 Len=7: 120,108,6,27,17,36,40?
SQL> select dump(col_timestamp) from test_timestamp1;?
DUMP(COL_TIMESTAMP)?
--------------------------------------------------------------------------------?
Typ=180 Len=11: 120,108,6,27,17,36,40,17,249,15,24?
現(xiàn)在可以看到timestamp(9)占用了11個(gè)字節(jié)的空間。后面額外的四個(gè)字節(jié)包括了小數(shù)秒數(shù)。?
3.3.2 TIMESTAMP 相減?
將兩個(gè)Date相減的結(jié)果是一個(gè)number.而將兩個(gè)timestamp相減的結(jié)果是一個(gè)INTERVAL值?
SQL> create table test_timestamp2(time1 timestamp(9), time2 timestamp(9));?
Table created?
SQL> insert into test_timestamp2 values(to_timestamp('2008-06-29 01:02:01.100000','yyyy-mm-dd hh24:mi:ss.ff'),to_timestamp('2008-07-29 02:03:02.000000','yyyy-mm-dd hh24:mi:ss.ff'))?
? 2? ;?
1 row inserted?
SQL> select time2 - time1 from test_timestamp2;?
TIME2-TIME1?
---------------------------------------?
+000000030 01:01:00.900000000?
結(jié)果表示兩個(gè)時(shí)間之間相隔的天數(shù),小時(shí)數(shù),分?jǐn)?shù),秒數(shù).?
相差30天1小時(shí)1分0.9秒?
有時(shí)我們需要得到兩個(gè)時(shí)間之前相關(guān)的年數(shù)和月數(shù).?
SQL> select numtoyminterval(months_between(time2,time1),'month') years_months, time2 -?
? 2? add_months(time1 , trunc(months_between(time2,time1))) days_hours from test_timestamp2;?
YEARS_MONTHS??????????????????????????? DAYS_HOURS?
--------------------------------------- ---------------------------------------?
+000000000-01?????????????????????????? +000000000 01:01:01.000000000?
在計(jì)算時(shí),分,秒間隔時(shí)我們注意到,使用add_months之后,小數(shù)秒就丟掉了.?
如果要保留集小數(shù)秒,我們就需要使用numtoyminterval函數(shù)?
SQL> select numtoyminterval(months_between(time2,time1),'month') years_months, time2 -(time1 + numtoyminterval(trunc(months_between(time2,time1)),'month')) day_hours from test_timestamp2;?
YEARS_MONTHS??????????????????????????? DAY_HOURS?
--------------------------------------- ---------------------------------------?
+000000000-01?????????????????????????? +000000000 01:01:00.900000000?
§3.4? TIMESTAMP WITH TIME ZONE?
? TIMESTAMP WITH TIME ZONE類型是TIMESTAMP的子類型,增加了時(shí)區(qū)支持。?
SQL> create table test_timezone(col_ts timestamp, col_tz timestamp with time zone);?
Table created?
SQL> insert into test_timezone values(systimestamp, systimestamp);?
1 row inserted?
SQL> select dump(col_tz) from test_timezone;?
DUMP(COL_TZ)?
--------------------------------------------------------------------------------?
Typ=181 Len=13: 120,108,6,27,9,55,24,43,209,96,112,28,60?
SQL> select dump(col_ts) from test_timezone;?
DUMP(COL_TS)?
--------------------------------------------------------------------------------?
Typ=180 Len=11: 120,108,6,27,17,55,24,43,209,96,112?
占用13字節(jié)的存儲(chǔ)空間,最后兩位用于保存時(shí)區(qū)信息。?
在timestamp類型中,對(duì)時(shí),分,秒的存儲(chǔ)采用了加1法。?
在timestamp with time zone上執(zhí)行timestamp運(yùn)算時(shí),oracle自動(dòng)把兩個(gè)類型首先轉(zhuǎn)換為UTC時(shí)間,然后再執(zhí)行運(yùn)算。?
§3.5 TIMESTAMP WITH LOCAL TIME ZONE?
這個(gè)類型保存進(jìn)數(shù)據(jù)庫(kù)時(shí)會(huì)先轉(zhuǎn)換成數(shù)據(jù)庫(kù)時(shí)區(qū)再進(jìn)行保存.?
SQL> create table test_timeltz(col_date date, timetz timestamp with time zone, timeltz timestamp with local time zone);?
Table created?
SQL> insert into test_timeltz values(timestamp'2008-06-29 12:03:22.111 US/Pacific',timestamp'2008-06-29 12:03:22.111 US/Pacific',timestamp'2008-06-29 12:03:22.111 US/Pacific');?
1 row inserted?
SQL> select dbtimezone from dual;?
DBTIMEZONE?
----------?
+08:00?
SQL> select * from? test_timeltz;?
COL_DATE??? TIMETZ?????????????????????????????????????????????????????????????????????????? TIMELTZ?
----------- -------------------------------------------------------------------------------- --------------------------------------------------------------------------------?
2008-6-29 1 2008-06-29 12:03:22.111000 US/PACIFIC??????????????????????????????????????????? 2008-06-30 03:03:22.111000?
SQL> select dump(col_date), dump(timetz), dump(timeltz) from test_timeltz;?
DUMP(COL_DATE)?????????????????????????????????????????????????????????????????? DUMP(TIMETZ)???????????????????????????????????????????????????????????????????? DUMP(TIMELTZ)?
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --------------------------------------------------------------------------------?
Typ=12 Len=7: 120,108,6,29,13,4,23?????????????????????????????????????????????? Typ=181 Len=13: 120,108,6,29,20,4,23,6,157,185,192,137,156?????????????????????? Typ=231 Len=11: 120,108,6,30,4,4,23,6,157,185,192?
請(qǐng)注意:?
第一列,類型為date,只存儲(chǔ)了日期和時(shí)間.時(shí)區(qū)和小數(shù)秒已經(jīng)丟失了.不會(huì)執(zhí)行時(shí)區(qū)轉(zhuǎn)換.?
第二列:類型為timestamp with time zone.保留了時(shí)區(qū)信息.并規(guī)范化了該時(shí)區(qū)相應(yīng)的UTC時(shí)間.?
第三列:類型為timestamp with local time zone.進(jìn)行了轉(zhuǎn)換,把插入的時(shí)間轉(zhuǎn)為了數(shù)據(jù)庫(kù)時(shí)區(qū)的時(shí)間.?
timestamp with local time zone也是不需要記錄時(shí)區(qū)信息的.它占用7-11個(gè)字節(jié).?
一旦你的數(shù)據(jù)表中有一列使用了timestamp with local time zone,你就不能再改變數(shù)據(jù)庫(kù)時(shí)區(qū).?
通用協(xié)調(diào)時(shí)(UTC, Universal Time Coordinated) ,UTC與格林尼治平均時(shí)(GMT, Greenwich Mean Time)一樣,都與英國(guó)倫敦的本地時(shí)相同. 北京時(shí)區(qū)是東八區(qū),領(lǐng)先UTC八個(gè)小時(shí)?
§3.6? INTERVAL?
用于表示一段時(shí)間或一個(gè)時(shí)間間隔的方法.在前面有多次提過.INTERVAL有兩種類型.?
YEAR TO MONTH 能存儲(chǔ)年或月指定的一個(gè)時(shí)間段.?
DATE TO SECOND存儲(chǔ)天,小時(shí),分鐘,秒指定的時(shí)間段.?
在前面用到的兩個(gè)函數(shù)numtoyminterval 和numtodsinterval就是創(chuàng)建interval最好的方法.?
另外extract 函數(shù)可以很容易的獲得時(shí)間的每個(gè)部分.?
SQL> select extract(day from? time2-time1) day, extract(hour from time2 - time1) hour,?
? 2? extract (minute from time2 - time1) minute,extract (second from time2 - time1) second from?
? 3? test_timestamp2;?
?????? DAY?????? HOUR???? MINUTE???? SECOND?
---------- ---------- ---------- ----------?
??????? 30????????? 1????????? 1??????? 0.9?
3.6.1 Interval year to month?
? 語(yǔ)法:?
Interval year(n) to month?
N表示年數(shù)的位數(shù).取值:0~9 .默認(rèn)為2,表示年數(shù)為0 ~ 99?
如果要產(chǎn)生一個(gè)1年2個(gè)月的時(shí)間段.用numtoyminterval是最方便的.?
SQL> select (numtoyminterval(1,'year') + numtoyminterval(2,'month')) yminterval from dual;?
YMINTERVAL?
---------------------------------------?
+000000001-02?
或者是:?
SQL> select? numtoyminterval(1 * 12 + 2,'month') yminterval from dual;?
YMINTERVAL?
---------------------------------------?
+000000001-02?
另外可以使用 to_yminterval(‘1-2’)函數(shù)來進(jìn)行轉(zhuǎn)換.?
SQL> create table test_intervarym(col_interval interval year to month);?
Table created?
SQL> insert into test_intervarym values ( numtoyminterval(1 * 12 + 2,'month'));?
1 row inserted?
SQL> select * from test_intervarym;?
COL_INTERVAL?
---------------------------------------?
+01-02?
SQL> select dump(col_interval) from test_intervarym;?
DUMP(COL_INTERVAL)?
--------------------------------------------------------------------------------?
Typ=182 Len=5: 128,0,0,1,62?
INTERVAL year to month 采用固定5個(gè)字節(jié)進(jìn)行存儲(chǔ).最后一位為天數(shù)值.采用加60算法.所以計(jì)算是需要減去60.?
第一位為標(biāo)志位,標(biāo)志是否為正負(fù)數(shù).?
第二到第四位表示年數(shù).?
第五位表示日數(shù)?
3.6.2 INTERVAL DAY TO SECOND?
? 定義如下:?
? INTERVAL DAY(n) to second(m)?
? N為可選位數(shù),表示天數(shù)的位數(shù).可取值0~9,默認(rèn)為2位.?
? M是秒字段小時(shí)的位數(shù).取值0~9,默認(rèn)為6?
SQL> create table test_intervalds(col_ds interval day(9) to second(9));?
Table created?
SQL> insert into test_intervalds values(numtodsinterval(1,'second'));?
1 row inserted?
SQL> insert into test_intervalds values(numtodsinterval(1.000000001,'second'));?
1 row inserted?
SQL> select col_ds, dump(col_ds) from test_intervalds;?
COL_DS????????????????????????????????? DUMP(COL_DS)?
--------------------------------------- --------------------------------------------------------------------------------?
+000000000 00:00:01.000000000?????????? Typ=183 Len=11: 128,0,0,0,60,60,61,128,0,0,0?
+000000000 00:00:01.000000001?????????? Typ=183 Len=11: 128,0,0,0,60,60,61,128,0,0,1?
可見,這種類型也是采用固定11個(gè)字節(jié)來存儲(chǔ)的.?
第一位為標(biāo)志位,區(qū)分正負(fù)數(shù)?
第二到第四位表示天數(shù).?
第五位表示小時(shí)數(shù).時(shí),分,秒采用加60算法?
第六位表示分鐘數(shù),?
第七位表示秒數(shù).?
最后四位表示小數(shù)秒數(shù).?
第四部分 LOB類型?
§ 4.1? LOB類型?
4.1.1 LOB類型分類?
CLOB:字符LOB.用于存儲(chǔ)大量的文本信息.采用默認(rèn)字符集存儲(chǔ)?
NCLOB:用于存儲(chǔ)字符LOB,采用數(shù)據(jù)庫(kù)的國(guó)家字符集來存儲(chǔ)字符.而不是數(shù)據(jù)庫(kù)的默認(rèn)字符集.?
BLOB:二進(jìn)制LOB,存儲(chǔ)二進(jìn)大量的二進(jìn)制信息.存儲(chǔ)時(shí)不會(huì)進(jìn)行字符集轉(zhuǎn)換.?
CLOB和BLOG在ORACLE 10G中可存儲(chǔ)8TB字節(jié).?
BFILE:二進(jìn)制文件LOB,只是一個(gè)文件指針.具體的文件存儲(chǔ)在操作系統(tǒng)中.?
4.1.2 LOB類型存儲(chǔ)方式?
我們把CLOB,NCLOB,BLOB存儲(chǔ)在數(shù)據(jù)庫(kù)的內(nèi)部稱為內(nèi)部LOB.這些存儲(chǔ)方式都相似,所以可以一起進(jìn)行討論.?
SQL> create table test_lob (id int primary key,remark clob);?
Table created?
對(duì)于LOB列的創(chuàng)建有非常多的選項(xiàng).可以查ORACLE文檔.?
最簡(jiǎn)單的就是使用dbms_metadata來獲得它的完整的腳本.?
select dbms_metadata.get_ddl('TABLE','TEST_LOB') from dual;?
得到如下結(jié)果?
? CREATE TABLE "YUAN"."TEST_LOB"?
?? ( "ID" NUMBER(*,0),?
"REMARK" CLOB,?
PRIMARY KEY ("ID")?
? USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255?
? STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645?
? PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)?
? TABLESPACE "USERS"? ENABLE?
?? ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING?
? STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645?
? PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)?
? TABLESPACE "USERS"?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192 PCTVERSION 10?
? NOCACHE LOGGING?
? STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645?
? PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT))?
LOB列的定義可以有以下屬性.?
存儲(chǔ)的表空間,本例為USER.也就是說可以為L(zhǎng)OB單獨(dú)指定表空間.?
ENABLE STORAGE IN ROW 默認(rèn)的一個(gè)屬性?
CHUNK 屬性?
PCTVERSION 屬性?
NOCACHE 屬性.?
一個(gè)完整的STORAGE語(yǔ)句.?
可見,LOB類型之前介紹的數(shù)據(jù)類型相比要復(fù)雜得多了.?
當(dāng)我們創(chuàng)建了一個(gè)帶的LOB列的表后,我們可以從USER_SEGMENTS查到,數(shù)據(jù)庫(kù)增加了幾個(gè)段對(duì)象.?
SQL> select segment_name,segment_type from user_segments;?
SEGMENT_NAME????????????????????? SEGMENT_TYPE?
--------------------------------- ------------------?
BIN$nZwCJWDmQM+ygfB1U8tcIw==$0??? TABLE?
BIN$0jfW0nNQR/2JEQmbAmfcRQ==$0??? TABLE?
TEST_TIMESTAMP??????????????????? TABLE?
TEST_TIMESTAMP2?????????????????? TABLE?
TEST_TIMESTAMPWZ????????????????? TABLE?
TEST_TIMELTZ????????????????????? TABLE?
TEST_INTERVARYM?????????????????? TABLE?
TEST_INTERVALYM2????????????????? TABLE?
TEST_INTERVALDS?????????????????? TABLE?
TEST_LOB????????????????????????? TABLE?
SYS_LOB0000043762C00002$$???????? LOBSEGMENT?
SYS_IL0000043762C00002$$????????? LOBINDEX?
SYS_C004324?????????????????????? INDEX?
后面四個(gè)段空間對(duì)象.新增了四個(gè)物理段.普通表只會(huì)新增一個(gè)或兩個(gè)段對(duì)象.類型為TABLE和INDEX.?
而LOB列則額外新增了兩個(gè)段對(duì)象,類型為L(zhǎng)OBSEGMENT和LOBINDEX.?
SYS_C004324是一個(gè)索引段,因?yàn)槲覀冇幸涣袨橹麈I.?
作為普通字段,數(shù)據(jù)就存放在表段中.索引就放在索引段中.?
而對(duì)于LOB數(shù)據(jù),數(shù)據(jù)并不是存在表段中,而是存放在LOBSEGMENT段中.(有些情況下是存放在表test_lob中的.后面會(huì)講)?
LOBINDEX用于指向LOB段,找出其中的某一部分.?
所以存儲(chǔ)在表中的LOB存儲(chǔ)的是一個(gè)地址,或者說是一個(gè)指針,也可以說是一個(gè)LOB定位器(LOB locator).?
存儲(chǔ)在LOBindex中的應(yīng)該是每一個(gè)LOB行的地址.數(shù)據(jù)是具體存儲(chǔ)在LOBSEGMENT中的.?
我們先從TEST_LOB的LOB列中找到一個(gè)地址,然后在LOBINDEX中來查找這些字節(jié)存儲(chǔ)在哪里.然后再訪問LOBSEGMENT.由此我們可以把lobindex和lobsegment想成是一個(gè)主/細(xì)表的關(guān)系.?
實(shí)際上lob列中存的是一個(gè)地址段.然后在lobindex找到所有的地址段.然后在lobSegment中把所有地址段的值都讀取了來?
4.1.3 LOB類型存儲(chǔ)參數(shù)介紹?
在此,我們已經(jīng)基本了解了LOB是怎么存儲(chǔ)的.我們也從腳本中看到了LOB類型的參數(shù).現(xiàn)在我們就來了解這些參數(shù)?
1. LOB表空間?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS"?
在test_lob表中的create語(yǔ)句中包含上面的語(yǔ)句.這里指定的表空間指的是存儲(chǔ)lobindex 和lobsegment的表空間.也就是說,存放lob數(shù)據(jù)與LOB列所在的表是可以在不同的表空間的.?
數(shù)據(jù)表和LOB存放在不同的表空間.?
為什么LOB數(shù)據(jù)會(huì)放在不同的表空間呢?這主要還是管理和性能的問題.?
LOB數(shù)據(jù)類型代表了非常巨大的容量.在ORACLE 10G之前,LOB列可以存放4GB字節(jié)的數(shù)據(jù).在ORACLE 10G 中LOB類型可以存放8TB字節(jié)的數(shù)據(jù).這是非常龐大的數(shù)據(jù).?
所以就有必要為L(zhǎng)OB數(shù)據(jù)使用一個(gè)單獨(dú)的表空間,對(duì)于備份和恢復(fù)以及空間管理.你甚至可以讓LOB數(shù)據(jù)使用另外一個(gè)區(qū)段大小,而不是普通表數(shù)據(jù)所用的區(qū)段大小.?
? 另外從I/O性能的角度考慮.LOB是不在緩沖區(qū)緩存中進(jìn)行緩存.因此每個(gè)LOB的讀寫,都會(huì)產(chǎn)生物理I/O.正因?yàn)槿绱?如果我們很清楚在實(shí)際的用戶訪問中,有些對(duì)象會(huì)比大部分其它對(duì)象需要花費(fèi)更多的物理I/O,那么就需要把這些對(duì)象分離到其它的磁盤.?
另外,lobindex 和lobsegment是在同一個(gè)表空間中的.不可以把lobindex和lobsegment放在不同的表空間中.在oracle 8i之前版本,允許將lobindex和lobsegment放在不同的表空間中.?
2. IN ROW 語(yǔ)句?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS" ENABLE STORAGE IN ROW?
我們已經(jīng)了解了LOB類型的存儲(chǔ)結(jié)構(gòu),但是這種結(jié)構(gòu)會(huì)帶來額外的磁盤訪問.不管是讀還是寫都會(huì)比普通數(shù)據(jù)類型要慢及帶來更多的物理I/O.?
針對(duì)這種情況,ORALCE作出了個(gè)改進(jìn)就是IN ROW 語(yǔ)句.?
使用ENABLE STORAGE IN ROW從字面上理解就是允許行內(nèi)存儲(chǔ).當(dāng)LOB的內(nèi)容小于4000字節(jié)時(shí),就把數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)表中的,即LOB數(shù)據(jù)與數(shù)據(jù)表都是同一個(gè)表空間中.這里的LOB就相當(dāng)于VARCHAR2一樣,這里L(fēng)OB列的數(shù)據(jù)還可以進(jìn)入緩沖區(qū)進(jìn)行存儲(chǔ).當(dāng)LOB內(nèi)容超過了4000字節(jié)后,就會(huì)把數(shù)據(jù)移到lobsegment中去.?
當(dāng)定義一個(gè)LOB列時(shí),它的大小一般都是小于4000字節(jié)的,啟用IN ROW 是非常重要的.?
如果要禁用IN ROW ,就使用DISALBE STORAGE IN ROW?
3. CHUNK 參數(shù)?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192?
CHUNK 意為大塊,塊.是指LOB存儲(chǔ)的單位.指向LOB數(shù)據(jù)的索引會(huì)指向各個(gè)數(shù)據(jù)塊.CHUNK是邏輯上連續(xù)的一組數(shù)據(jù)塊.CHUNK是指LOB的最小分配單元.而數(shù)據(jù)庫(kù)的最小內(nèi)存分配單元是數(shù)據(jù)塊(BLOCK).CHUNK大小必須是ORACLE塊大小的整數(shù)倍.?
我們先來了解一下LOB與CHUNK的關(guān)系.?
1. 每一個(gè)LOB實(shí)例(即每一行的LOB值)會(huì)至少占用一個(gè)CHUNK.?
用我們本節(jié)的數(shù)據(jù)表test_lob為例,remark列為L(zhǎng)OB類型.?
假設(shè)該表有1000行數(shù)據(jù),每一行的remark列的值大小都為7KB.?
這樣數(shù)據(jù)庫(kù)就會(huì)分配1000個(gè)CHUNK.如果CHUNK的大小設(shè)置是64KB,就會(huì)分配1000個(gè)64KB的CHUNK.如果CHUNK的大小為8KB,就分配1000個(gè)8KB的CHUNK.?
重要的一點(diǎn)就是一個(gè)CHUNK只能由一個(gè)LOB對(duì)象使用.這有一點(diǎn)像CHAR這種定長(zhǎng)類型.如果把CHUNK設(shè)為64KB,而實(shí)際上我們每一個(gè)LOB對(duì)象只有7KB的大小,每一列浪費(fèi)57KB的空間.1000列就浪費(fèi)了55M的空間.而把CHUNK設(shè)為8KB,1000列大約浪費(fèi)1M的空間.?
我們還知道lobindex,且于指向各個(gè)塊.它會(huì)記錄每個(gè)塊的地址.所以當(dāng)塊越多時(shí),索引就越大,索引越大時(shí),讀寫就會(huì)更慢.整體的性能就會(huì)降低.?
比如每個(gè)列的LOB字段實(shí)際值大約8M,使用8KB的CHUNK.那么就需要1024個(gè)CHUNK.那么在lobindex中就會(huì)有1024條記錄,用來指向這些CHUNK.?
指定CHUNK值,影響到性能和空間.?
如果CHUNK過大,就會(huì)白白浪費(fèi)存儲(chǔ)空間,如果CHUNK過小,就會(huì)降低性能.?
所以我們需要在空間和性能上進(jìn)行取舍和折中.?
4. PCTVERSION 語(yǔ)句?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192 PCTVERSION 10?
PCTVERSION用于控制LOB的讀一致性.普通字段都會(huì)有UNDO記錄的.而lobsegment是沒有undo記錄的.而是直接在lobsegment本身中維護(hù)停息的版本.lobindex會(huì)像其它段一樣生成undo記錄.但是lobsegment不會(huì).?
修改一個(gè)LOB對(duì)象時(shí),oracle會(huì)分配一個(gè)新的CHUNK,而來的CHUNK會(huì)被保留下來.如果事務(wù)正常的提交了,lobindex就像指向新的CHUNK.如果事務(wù)被回滾了,lobindex就再指回原來的CHUNK.所以u(píng)ndo維護(hù)是在LOB段自身中實(shí)現(xiàn)的.?
這樣一來,就會(huì)有非常多的無用的CHUNK被開銷了.這也是非常大的空間損耗.這些CHUNK指的是數(shù)據(jù)的舊版本信息.那如何來控制這些舊版本數(shù)據(jù)占用的空間呢?這就是PCTVERSION的作用.也就是說用多少額外的空間來存儲(chǔ)舊版本數(shù)據(jù).我們可以看到默認(rèn)的值是10%.如果你確實(shí)經(jīng)常修改LOB,那么就需要把它設(shè)為10%就不夠了,需要增加這個(gè)值.?
5. CACHE參數(shù)?
LOB ("REMARK") STORE AS (?
? TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192 PCTVERSION 10?
? NOCACHE?
除了NOCACHE外,這個(gè)選項(xiàng)還可是CACHE和CACHE READS.這個(gè)參數(shù)控制lobsegment數(shù)據(jù)是否存儲(chǔ)在緩沖區(qū)的緩存中.默認(rèn)為NOCACHE,也就是每次訪問都是從磁盤直接讀取寫.?
CACHE READS允許緩存從磁盤讀的LOB數(shù)據(jù).但是寫入LOB數(shù)據(jù)是直接寫進(jìn)磁盤的.?
CACHE則是允許讀和寫都能緩存LOB數(shù)據(jù).?
有些情況下,LOB字段只有幾KB大小,進(jìn)行緩存就非常有用了.如果不緩存,當(dāng)用戶更新LOB字段時(shí),還必須進(jìn)行等待,從磁盤直接讀數(shù)據(jù)和寫數(shù)據(jù).?
如果要修改緩存設(shè)置可以用下面的語(yǔ)句?
ALTER TABLE test_lob modify LOB(remark) (CACHE);?
ALTER TABLE test_lob modify LOB(remark) (NOCACHE);?
ALTER TABLE test_lob modify LOB(remark) (CACHEREADS);?
但是對(duì)于大數(shù)據(jù)量的LOB讀寫,比如超過了20M.是沒有理由把它放進(jìn)緩存的?
§ 4.2 BFILE?
? BFILE類型只是操作系統(tǒng)上一個(gè)文件的指針.用于對(duì)存儲(chǔ)在操作系統(tǒng)中的文件提供只讀訪問.?
使用BFILE時(shí),還可以使用一個(gè)DIRECTORY 對(duì)象.DIRECTORY 是將一個(gè)操作系統(tǒng)目錄映射到數(shù)據(jù)庫(kù)的一個(gè)串.以便于提供可移值性.?
SQL> create table test_bfile(id int primary key, moviefile bfile);?
Table created?
SQL> create or replace directory movie_directory as 'D:/movie';?
Directory created?
SQL> insert into test_bfile values(1,bfilename('movie_directory','英雄.dat'));?
1 row inserted?
對(duì)BFILE的操作需要使用DBMS_LOB包來進(jìn)行.提供了一系統(tǒng)方法和函數(shù)?
第五部分 LONG類型?
LONG是一種已經(jīng)被棄用的數(shù)據(jù)類型,LOB類型是它的替代品.所以留在LOB之后進(jìn)行討論.?
我們只需要簡(jiǎn)單的了解即可.為什么ORACLE還保留這種類型,只是為了向后兼容,在新的數(shù)據(jù)庫(kù)設(shè)計(jì)是,不要再使用LONG類型列.?
LONG類型有兩種:?
?? LONG :能存儲(chǔ)2GB的字符?
?? LONG RAW:能存儲(chǔ)最多2GB的二進(jìn)制數(shù)據(jù).?
我們只需要對(duì)LONG類型的限制進(jìn)行了解即可.?
LONG/LONG RAW 類型 CLOB/BLOB類型?
一個(gè)表只能有一個(gè)LONG/LONG RAW列 一個(gè)表可以有最多1000個(gè)LOB類型列?
不能用于用戶自定義類型 可以用于用戶自定義類型?
WHERE中不能引用LONG類型 可以?
除了NOT NULL,完整性約束中不能引用 可以?
不支持分布式事務(wù) 支持?
不能使用基本或高級(jí)復(fù)制技術(shù) 可以?
不能在GROUP BY,ORDER BY,CONNECT BY,DISTINCT,UNIQUE,INTERSECT,MINUS,UNION中使用 可以通過函數(shù)來轉(zhuǎn)換成一個(gè)標(biāo)量SQL類型來支持?
PL/SQL函數(shù)和過程中不能作為參數(shù) 可以?
不能應(yīng)用于內(nèi)置函數(shù),如SUBSTR 可以?
CREATE TABLE AS SELECT不能使用LONG類型 支持?
在有LONG類型的表中不能進(jìn)行移動(dòng)表空間 可以?
總之一句話,新系統(tǒng)不應(yīng)該再使用LONG類型.?
老系統(tǒng)如果有的表的某些字段是LONG類型,要注意它的限制?
第六部分 ROWID?
ROWID 就是數(shù)據(jù)庫(kù)中一行的地址,用于記錄數(shù)據(jù)存儲(chǔ)的一些屬性,包括:記錄存儲(chǔ)所在的數(shù)據(jù)文件(file#),所屬的數(shù)據(jù)庫(kù)對(duì)象(obj#),所在的數(shù)據(jù)塊號(hào)(block_no#),以及在表中的行號(hào)。這些屬性就構(gòu)成了Oracle 的ROWID.?
我們需要注意的是在數(shù)據(jù)表中并沒有一列來專門記錄ROWID。?
另外還有一個(gè)UROWID,它用于表,是行主鍵的一個(gè)表示,基于主鍵生成.一般是索引組織表在使用。索引組織表是沒有ROWID的。?
不管是ROWID還是UROWID,數(shù)據(jù)表都沒有專門的一列來記錄。?
我們把這兩種類型稱為偽列。?
SQL> create table test_rowid (id number(38));?
Table created?
SQL> insert into test_rowid values(1);?
1 row inserted?
SQL> select rowid, id from test_rowid;?
ROWID?????????????????????????????????????????????????? ID?
------------------ ---------------------------------------?
AAAKsAAAEAAAAC+AAA?????????????????????????????????????? 1?
因?yàn)镽OWID可以唯一的標(biāo)識(shí)一條記錄,所以索引中存儲(chǔ)了ROWID值,通過索引訪問記錄,其實(shí)也就是通過從索引獲得ROWID,再根據(jù)ROWID定位數(shù)據(jù)表中的記錄。?
但是當(dāng)對(duì)表進(jìn)行分區(qū)移動(dòng)之后,索引就需要重建,因?yàn)榇鎯?chǔ)位置已經(jīng)發(fā)生了變化,索引中的ROWID已經(jīng)不能再定位到新的數(shù)據(jù)了。?
ORACLE 的ROWID一直在不斷變化。?
在ORACLE 6中,ROWID使用6bit來表示文件號(hào)。?
在ORACLE 8,ROWID的組成是FFFF.BBBBBBBB.RRRR。占用6個(gè)字節(jié)。?
10bit 的file#,22bit的block#,16bit的row #?
在ORACLE 9中,Oracle 為ROWID引入了數(shù)據(jù)對(duì)象號(hào)的概念dataobj#.?
現(xiàn)在ROWID格式變?yōu)镺OOOOO.FFF.BBBBBB.RRR。最新的ROWID采用Base64編碼,一共有18位,代表80位二進(jìn)制數(shù),其中:O為數(shù)據(jù)對(duì)象號(hào),F是文件號(hào),B是塊號(hào),R是行號(hào)?
32 bit dataobj#+10bit rfile#,+22 bit block# +16bit row#?
在以前ROWID是保持不變的,但現(xiàn)在ROWID是會(huì)發(fā)生改變的。如:?
把一行從一個(gè)分區(qū)移到另一個(gè)分區(qū)?
使用閃回表(flashback table)命令將一個(gè)數(shù)據(jù)表恢復(fù)到以前的某個(gè)時(shí)間點(diǎn)?
對(duì)分區(qū)進(jìn)行操作,如:移動(dòng),分解和合并?
對(duì)段進(jìn)行收縮?
這些操作都會(huì)使ROWID發(fā)生變化,所以我們不應(yīng)該把ROWID來作為唯一標(biāo)識(shí)。而是使用一個(gè)單獨(dú)的列為主鍵用來作數(shù)據(jù)行的唯一標(biāo)識(shí)。另外主鍵約束可以實(shí)現(xiàn)引用完整性。而ROWID是無法做到的。?
筆者曾經(jīng)使用ROWID排序來實(shí)現(xiàn)按數(shù)據(jù)的寫入順序來顯示數(shù)據(jù)。這在大多數(shù)情況下是可以做的,但是如果以后因?yàn)榫S護(hù)數(shù)據(jù)庫(kù),對(duì)分區(qū)進(jìn)行操作后,這樣做是不可行的。?
所以應(yīng)該使用單獨(dú)的列來記錄數(shù)據(jù)的寫入順序。?
ROWID類型的主要用途是與數(shù)據(jù)庫(kù)進(jìn)行交互時(shí),可以快速的指向某一行。比如使用ROWID更新某一行等。可以不通過索引而快速的找到某行記錄。并且可以很快的進(jìn)行行數(shù)據(jù)的驗(yàn)證。?
總結(jié)
以上是生活随笔為你收集整理的Oracle 数据类型及存储方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle中的Raw类型解释
- 下一篇: goldengate 故障及解决方法汇总