Oracle 数据类型及存储方式(袁光东 原创)
概述
通過實(shí)例,全面而深入的分析oralce 10G的基本數(shù)據(jù)類型及它們的存儲(chǔ)方式。從實(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
注意:最多只能插入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
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碼??梢?strong>使用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
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
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)證。
?
轉(zhuǎn)載于:https://www.cnblogs.com/askjacklin/archive/2012/06/04/2534580.html
總結(jié)
以上是生活随笔為你收集整理的Oracle 数据类型及存储方式(袁光东 原创)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spherical Harmonics
- 下一篇: MOSS2010 标准版与企业版的区别