11. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
數字)
INTEGER
INTEGER 是 NUMBER 的子型態(SubType),其實等同 NUMBER(38,0),可以用來儲存整數,若數值有
小數,則會被四捨五入。如果需要限制數值的精確度,則建議使用 NUMBER(P,0),因為 INTEGER 的精
確度固定為 38。
--為方便觀察結果,往後的範例皆使用sys身份進行操作
SQL> CONNECT / AS SYSDBA
SQL> CREATE TABLE frank.t1_int (a INTEGER);
SQL> DESC frank.t1_int
Name Null? Type
----------------------------------------------------------------- -------- ----------------------------------------
A NUMBER(38)
SQL> CREATE TABLE frank.t2_int (a INTEGER);
SQL> DESC frank.t2_int
Name Null? Type
----------------------------------------------------------------- -------- ----------------------------------------
A NUMBER(38)
SQL> INSERT INTO t1_int VALUES(1234567890123456789012345678901234567890);
INSERT INTO t1 VALUES(1234567890123456789012345678901234567890)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
/*1234567890123456789012345678901234567890共40位數*/
SQL> INSERT INTO t1_int VALUES(12345678901234567890123456789012345678);
SQL> INSERT INTO t2_int VALUES(12345678901234567890123456789012345678.9);
/*小數點後四捨五入*/
SQL> COMMIT;
SQL> SELECT * FROM frank.t1_int;
A
---------------
1.2346E+37
/*只是呈現的樣式如此,其實真的存入12345678901234567890123456789012345678的數值*/
SQL> SELECT * FROM frank.t2_int;
A
---------------
1.2346E+37
/*只是呈現的樣式如此,其實真的存入12345678901234567890123456789012345679的數值,因為小數點
後四捨五入,切記*/
SQL> SELECT DUMP(a) FROM frank.t1_int;
DUMP(A)
13. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> CREATE TABLE frank.t1_flt(a FLOAT(10,2));
CREATE TABLE frank.t1_flt(a FLOAT(10,2))
*
ERROR at line 1:
ORA-00907: missing right parenthesis
/*FLOAT不能指定Scale*/
SQL> CREATE TABLE frank.t1_flt(a NUMBER,b FLOAT,c FLOAT(5),d FLOAT(9),e FLOAT(10));
SQL> DESC frank.t1_flt
Name Null? Type
----------------------------------------------------------- -------- ----------------------------------------
A NUMBER
B FLOAT(126)
C FLOAT(5)
D FLOAT(9)
E FLOAT(10)
/*FLOAT的最大二進位精確度為126*/
SQL> INSERT INTO frank.t1_flt VALUES(123.45,123.45,123.45,123.45,123.45);
SQL> SELECT * FROM frank.t1_flt;
A B C D E
------------- ------------ ----------- ---------- ----------
123.45 123.45 120 123 123.5
/* FLOAT(126)的十進位精確度為126*0.30103=37.92978,進位後為38(十進位精確度)。
FLOAT(9)的十進位精確度為9*0.30103=2.70927,進位後為3。123.45=1.2345*10^2,因為數值有四捨五
入,所以4以後被捨棄,結果為123。
FLOAT(10)的十進位精確度為10*0.30103=3.0103,進位後為4。123.45=1.2345*10^2,因為數值有四捨
五入,導致4變成5,因此結果為123.5。*/
8.2.3 日期型態
日期型態顧名思意是用來儲存日期資料,不過並不是使用一般所看到的格式(2009-01-01)直接存到資料
庫中,而是先將日期轉換為 Julian Day(儒略日,Oracle 採用天文學的定義),再將資料儲存到資料庫。
Julian Day 從西元前 4712 年 1 月 1 日中午開始為 1,以後每過一天就加上 1。所以如果將 2004-09-
15 改為 Julian Day 值則為 2453264。Oracle 資料庫中合法的日期範圍由 4712 BC 1 月 1 日到 9999
AD 12 月 31 日。
DATE
DATE 是最常被使用的日期型態,它可以存放世紀、年、月、日、時、分、秒的資訊。固定需要 7 個
Bytes 空間儲存 DATE 型態的資料。
SQL> SELECT TO_CHAR(TO_DATE('2004-09-15','YYYY-MM-DD'),'J') julian_days FROM dual;
JULIAN_
-----------
2453264
SQL> SELECT TO_CHAR(TO_DATE('01-01-4712 BC','DD-MM-YYYY BC'),'J') julian_days FROM dual;
JULIAN_
-----------
0000001
SQL> SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS') FROM dual;
14. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
TO_CHAR(SYSDATE,'YY
--------------------------------
2009-03-10 11:59:53
TIMESTAMP
從 Oracle9i 開始,Oracle 推出一種新的時間型態:TIMESTAMP。並且可以分成 TIMESTAMP、
TIMESTAMP WITH TIME ZONE 與 TIMESTAMP WITH LOCAL TIME ZONE 三種。
TIMESTAMP:可以儲存世紀、年、月、日、時、分、秒以及小數秒(最多可達小數點後 9 位)。需要 11
個 Bytes 空間儲存 TIMESTAMP 型態的資料。
TIMESTAMP WITH TIME ZONE:除了儲存 TIMESTAMP 的所有資訊外,另外還儲存時區(TIME ZONE)
資訊,所以需要多達 13 個 Bytes 的空間。
TIMESTAMP WITH LOCAL TIME ZONE:所欲儲存的 TIMESTAMP 資料先依資料庫端的時區,轉換為
資料庫端的 TIMESTAMP 格式,才儲存到資料庫中。當客戶端讀取該時間資訊時,自動將時間資料依客
戶端的時區資料轉換為 TIMESTAMP 格式呈現。需要 11 個 Bytes 的空間存放資料。
SQL> CREATE TABLE frank.t1_times(a TIMESTAMP,b TIMESTAMP WITH TIME ZONE,c TIMESTAM
P WITH LOCAL TIME ZONE);
SQL> INSERT INTO frank.t1_times VALUES(SYSTIMESTAMP,SYSTIMESTAMP,SYSTIMESTAMP);
SQL> SELECT SESSIONTIMEZONE FROM dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00 /*目前SESSION的時區*/
SQL> SELECT * FROM frank.t1_times;
------------------------------------------------------------------
12-MAR-09 02.23.49.029445 PM
12-MAR-09 02.23.49.029445 PM +08:00
12-MAR-09 02.23.49.029445 PM
SQL> ALTER SESSION SET TIME_ZONE='-07:00';
/*將SESSION的時區改為-07:00*/
SQL> SELECT * FROM frank.t1_times;
-----------------------------------------------------------------
12-MAR-09 02.23.49.029445 PM
12-MAR-09 02.23.49.029445 PM +08:00
11-MAR-09 11.23.49.029445 PM
/*TIMESTAMP WITH LOCAL TIME ZONE的時間會自動依客戶端的時區而調整*/
8.2.4 LOB(Large Object)
LOB 型態用來儲存大型資料。文字型態可以使用 CLOB(Character LOB)或 NCLOB(National Character
LOB),依所使用的字元集分類。二進位資料則可以使用 BLOB(Binary LOB)或 BFILE(Binary FILE)。
CLOB、NCLOB 與 BLOB 最大可儲存(4Giga Bytes-1)*Block_size 的資料,而 BFILE 最大可達 4Giga
Bytes。
文字大型物件(CLOB/NCLOB)
當資料大於 4000Bytes 時,一般的 VARCHAR2 或 NVARCHAR2 已經無法容納這麼大的資料。因此必
須使用 CLOB 或 NCLOB 來存放這些文字型態的資料。
15. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
二進位大型物件(BLOB)
當二進位資料超過 2000Bytes 時,便不能使用 RAW 的型態儲存,必須改用 BLOB 的型態。必須使用
DBMS_LOB 來操作其內容,不能直接使用 SQL 指令操作。
二進位檔案(BFILE)
BFILE 是用來儲存資料,與 CLOB/BLOB 不同的地方在於:
1.資料存放的位置不在資料庫中,而是在檔案系統的檔案中。所以 BFILE 欄位值只存放相對的檔案位置
(目錄物件名字與檔案名字),而不是真正的資料內容。BFILE 所使用的檔案格式可以為 GIF、JEPG、
JPG、MEPG、MPEG2、TEXT、AVI 或其他的格式。
2.因此 BFILE 的內容不能使用 SQL 指令修改,只能用在查詢操作。如果要更改 BFILE 內容,必須使用
正確儲存格式的編輯器修改。例如 AVI 格式需要使用影片編輯軟體修改其內容。
BFILE 所使用的檔案最大不能超做 4G Bytes。
8.2.5 資料列 id(Rowid)
資料列 id(rowid)是一個虛擬的欄位值,它並不存在於資料庫中,所以不會佔用資料列的空間。當每筆資
料列產生後,就有一個唯一的資料列 id 存在。資料列 id 用來表示資料列在資料庫的相對位置,而不是絕
對位置,只要使用資料列 id 可以直接找到包含該資料列的資料區塊。因此透過資料列 id 存取資料庫是存
取速度最快的方法,也是成本最低的方法。
然而資料列 id 的格式對人類來說相當難以記憶,因此資料庫才提供索引,將對人類較有意義的欄位值與
該資料列的資料列 id 組成索引項目儲存在索引。之後只要提供索引鍵欄位值,就可以透過索引取得該資
料列得資料列 id,使用資料列 id 讀取資料列。然而這種方法較直接使用資料列 id 來的慢,而且需要索引
的空間與維護成本。
資料列 id 分為實體資料列 id(Physical ROWID)與邏輯資料列 id(Logical ROWID)。邏輯資料列 id 用在索
引組織表格上的索引,而其他非索引組織表格上的索引則使用實體資料列 id。
Oracle Database 11g 可以使用的實體資料列 id 有下面三種︰受限制的資料列 id(Restricted rowid),延
伸的資料列 id(Extended rowid),通用的資料列 id(Universal rowid)。
受限的資料列 id(RESTRICTED ROWID)
Oracle Database 7 所使用資料列 id 稱做受限的資料列 id,其顯示格式為 BBBBBB.RRRR.FFFF,如果
需要儲存則佔 6 Bytes(48 Bits)的空間。
其中 BBBBBB 為該資料列所在的資料區塊的號碼,需要佔 22 Bits 的空間,資料區塊號碼為資料檔的第
幾個資料區塊,而不是資料庫的第幾個資料區塊。RRRR 表示該資料列為資料區塊的第幾筆資料列,需要
佔 16 Bits 空間,資料列號碼由 0 開始。FFFF 為該資料列在資料庫的第幾個資料檔,需要佔 10 Bits 的
空間。因此可以得知 Oracle Database 7 的資料檔個數不能超過 2
10
-1 個(1023 個,因為 0 不能使用。),
當然也可以知道一個資料檔最多可以有 2
22
(400 萬)個資料區塊,每個資料區塊理論上可容納 2
16
(65536)
筆資料列。
當表格為非分區表格(Non-Partitioned Table)時,每個表格只有一個區段。因此平衡樹索引(B-tree Index)
的索引項目所使用資料列 id 為受限的資料列 id 即可。同時點陣圖索引(Bitmap Index)也使用受限的資料
列 id。
延伸的資料列 id(EXTENDED ROWID)
Oracle Database 8 時新增一種新的資料列 id 的格式,稱做延伸的資料列 id(Extended Rowid),從名字
來看就知道是因應受限的資料列 id 的不足。這種資料列 id 的格式為 OOOOOO.FFF.BBBBBB.RRR,如
果需要儲存則需要 10 Bytes(80 Bits)的空間。其中用 OOOOOO 為該區段(Segment)的物件
16. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
id(DATA_OBJECT_ID)佔 32 Bits,FFF 為表格空間的資料檔號碼(relative file number)佔 10 Bits,
BBBBBB 為該資料列所在的資料區塊的號碼佔 22 Bits,RRRR 為該資料列為該資料區塊的第幾筆資料列
佔 16 Bits。
延伸的資料列 id 與受限的資料列 id 的差異,為延伸的資料列 id 多儲存了區段的物件 id。以及延伸的資料
列 id 所使用的檔案號碼為相對的檔案號碼,而受限的資料列 id 所使用的檔案號碼為絕對的檔案號碼。相
對的檔案號碼表示這個資料檔案為表格空間的第幾個資料檔,絕對的檔案號碼表示此資料檔為資料庫的第
幾個資料檔。不過在整個資料庫的資料檔個數在 1023 以下時,相對的檔案號碼與絕對的檔案號碼一樣。
因此當表格的區段分別存放在不同表格空間時,便可以使用區段的物件 id 得知所在的表格空間。所以在
分區表格上建立索引,索引項目所使用的資料列 id 就是延伸的資料列 id。因為在分區表格下,每個分區
都是一個區段,也就是說分區表格由多個區段組成。
延伸的資料列 id 以 Base 64 編碼方式呈現,也就是資料列 id 將由 A-Z,a-z,0-9,+,/等字元所組成。
以下為字元與數值的對應表。
字
元
A B C D E F G H I J K L M
數
值
0 1 2 3 4 5 6 7 8 9 10 11 12
字
元
N O P Q R S T U V W X Y Z
字
元
13 14 15 16 17 18 19 20 21 22 23 24 25
符
號
a b c d e f g h i j k l m
字
元
26 27 28 29 30 31 32 33 34 35 36 37 38
字
元
n o p q r s t u v w x y z
數
值
39 40 41 42 43 44 45 46 47 48 49 50 51
字
元
0 1 2 3 4 5 6 7 8 9 10 + /
數
值
52 53 54 55 56 57 58 59 60 61 62 63 64
17. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
邏輯資料列 id(Logical ROWID)
邏輯資料列 id 用在索引組織表格(Index Organization Index)上的索引,因為在索引組織表格上的資料列
並不會永遠儲存在同一個位置,資料列將隨主鍵值而更換儲存位置。因此在索引組織表格上建立索引時,
使用邏輯資料列 id。邏輯資料列 id 由實體猜測位置(Physical Guess DBA)與主鍵值(Primary Key)所組成。
實體猜測位置是建立索引時,該筆資料列所在的資料區塊位置。
通用的資料列 id(UNIVERSAL ROWID)
通用的資料列 id 是 Oracle Database 8i 開始使用的資料列 id 型態,可以用來儲存實體資料列 id、邏輯資
料列 id 與外部表格(含非 Oracle 資料表)的資料列 id。通用的資料列 id 最大可達 4000 Bytes。
8.2.6 虛擬欄位
由 Oracle Database 11g 開始,可以宣告一種欄位型態:虛擬欄位,這種欄位並沒有真正的消耗儲存空
間,只有存在於表格定義中。當此欄位被查詢時,才會使用欄位定義的公式計算欄位值。欄位定義的公式
可以包括任何合法的運算以及函數。
18. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> CREATE TABLE frank.t1_vir
2 (a NUMBER,b NUMBER,c NUMBER GENERATED ALWAYS AS (a+b) VIRTUAL);
/*GENERATED ALWAYS是為語法容易看懂而存在,VIRTUAL也是相同用途。所以可以直接使用c NUMBE
R AS (a+b)即可*/
SQL> DESC frank.t1_vir /*這裡看不出來,是否有使用虛擬欄位*/
Name Null? Type
--------------------------------------------------------- ------ -------------
A NUMBER
B NUMBER
C NUMBER
SQL> SELECT column_name,data_type,data_default FROM dba_tab_columns
2> WHERE OWNER=’FRANK’ AND table_name='T1_VIR' ;
COLUMN_NAM DATA_TYPE DATA_DEFAULT
------------------- ----------------- --------------------------
A NUMBER
B NUMBER
C NUMBER "A"+"B" /*欄位公式*/
SQL> INSERT INTO frank.t1_vir VALUES(1,2,3); /*不能直接Insert/Update虛擬欄位的內容*/
INSERT INTO frank.t1_vir VALUES(1,2,3)
*
ERROR at line 1:
ORA-54013: INSERT operation disallowed on virtual columns
SQL> INSERT INTO frank.t1_vir VALUES(1,2);
INSERT INTO frank.t1_vir VALUES(1,2)
*
ERROR at line 1:
ORA-00947: not enough values
SQL> INSERT INTO frank.t1_vir(a,b) VALUES(1,2); /*必須如此才能新增成功*/
---或 INSERT INTO frank.t1_vir VALUES(1,2,DEFAULT);
1 row created.
SQL> SELECT * FROM frank.t1_vir;
A B C
----------- ---------- ----------
1 2 3
/*找出row真正存放的位置*/
SQL> SELECT dbms_rowid.rowid_relative_fno(rowid) file#,
2 dbms_rowid.rowid_block_number(rowid) block#
3 FROM frank.t1_vir WHERE a=1 AND b=2 AND c=3;
FILE# BLOCK#
------------ -----------
1 40378
--將Data Block內容dump到user tracefile
SQL> ALTER SYSTEM DUMP DATAFILE 1 BLOCK 40378;
19. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> SHOW PARAMETER user_dump_dest /*確認User tracefile的位置*/
NAME TYPE VALUE
----------------------------- --------- -------------------------------------------------------
user_dump_dest string /u03/diag/diag/rdbms/ora11g/ora11g/trace
/*找到伺服器處理作業(Server Process)在作業系統行程的號碼(OS Process ID)*/
SQL> SELECT spid FROM v$process
2 WHERE addr=(SELECT paddr FROM v$session
3 WHERE sid=(SELECT DISTINCT sid FROM v$mystat));
SPID
--------
5000 --這個行程號碼可能依環境不同而不同
SQL> !cat /u03/diag/diag/rdbms/ora11g/ora11g/trace/ora11g_ora_5000.trc
--tracefile格式為SID_ora_OSPID.trc
--從Data Block的內容來看,可以證明虛擬欄位-C,並未佔有任何空間。
block_row_dump:
tab 0, row 0, @0x1f85
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02 /*A欄位*/
col 1: [ 2] c1 03 /*B欄位*/
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 40378 maxblk 40378
使用者自行定義函數(User Defined Function)
當虛擬欄位用到使用者自行定義函數(User Defined Function)時,必須確定函數是明確性(Deterministic),
即只要輸入相同的參數值,函數就一定回傳相同的結果。
SQL> CREATE OR REPLACE minus_10(x NUMBER) RETURN NUMBER
2 IS
3 BEGIN
4 RETURN x-10;
5 END;
6 /
SQL> CREATE TABLE frank.t2 (a NUMBER,b NUMBER,c NUMBER AS (minus_10(a+b)) VIRTUA
L);
CREATE TABLE frank.t2 (a NUMBER,b NUMBER,c NUMBER AS (minus_10(a+b)) VIRTUAL)
*
ERROR at line 1:
ORA-30553: The function is not deterministic
SQL> CREATE OR REPLACE minus_10(x NUMBER) RETURN NUMBER DETERMINISTIC
2 IS
3 BEGIN
4 RETURN x-10;
5 END;
6 /
SQL> CREATE TABLE frank.t2_vir (a NUMBER,b NUMBER,c NUMBER AS (minus_10(a+b)) VIRT
UAL);
20. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
虛擬欄位的操作
虛擬欄位在 Oracle Database 11g 中,被當作一般的欄位,所以除了不能直接新增、異動其內容外,其
餘的操作如:當做主鍵、建立索引等都可以正常使用。
SQL> CREATE INDEX frank.t1_vir_c_idx ON t1_vir(c);
SQL> SELECT index_name,index_type FROM dba_indexes
2 WHERE owner=’FRANK’ AND table_name='T1_VIR';
INDEX_NAME INDEX_TYPE
------------------------------- --------------------------------------
T1_VIR_C_IDX FUNCTION-BASED NORMAL
/*從INDEX_TYPE可以得知,當建立索引在虛擬欄位上時,所建立的索引為Function-Based形態*/
SQL> ALTER TABLE frank.t1_vir ADD CONSTRAINT t1_vir_c_pk PRIMARY KEY(c);
/ 可以當作Primary KEY*/
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T1_VIR'); /*收集統計值*/
/*虛擬欄位也可以當作分區欄位,這種分區方式稱為Virtual Column-Based Partitioning*/
SQL> CREATE TABLE frank.t3_vir
2 (a NUMBER,b NUMBER,c NUMBER AS (a+b+100) VIRTUAL)
3 PARTITION BY RANGE (c)
4 (PARTITION p1 VALUES LESS THAN(150),
5 PARTITION p2 VALUES LESS THAN(250),
6 PARTITION p3 VALUES LESS THAN(350));
虛擬欄位的使用限制
目前虛擬欄位在使用上,還存在著一些限制:
1.僅適用在 Heap Organized Table 上,還不能用在 Index-Organized、Cluster、External、
Temporary 以及 Object 等表格上。
2.虛擬欄位不能參考其他的虛擬欄位,只能參考同一個表格中的真實欄位。
3.當使用函數時,此函數必須宣告為 Deterministic 以及回傳的資料型態必須為 Scalar。
4.虛擬欄位的資料型態不能為 PL/SQL 的資料形態、User Defined Type、LOB 以及 Long Raw 等型態。
8.3 表格的種類
表格用來儲存資料,根據不同型態的資料與存取的樣式,資料庫管理人員可以選擇不同型態的表格,來達
到最佳的儲存空間與存取效率。一般常見的表格型態為堆集組織表格(Heap Table),當然還可以依據不同
的需求建立如:分區表格(Partitioned Table)、索引組織表格(Index Organized Table)、叢集表格
(Cluster)、暫時表格(Temporary Table)與外部表格(External Table)等。
8.3.1 堆集組織表格(Heap Organized Table)
通常資料庫中最常見的表格型態就是堆集表格(Heap Table)也可以稱作一般表格(Regular Table)。當使用
這種表格時,資料庫管理人員無法控制資料列(Row)的位置,資料列根據可用區塊串列(Freelists)或自動
區段空間管理(ASSM)機制決定存放的資料區塊,也可以說在此種表格中資料是無特別的順序。當建立表
格時,基本上需要知道這個表格將放在哪個綱要(Schema)、哪個表格空間。當然表格的名字與欄位名稱
與資料型態也不可以不知道。
建立表格前,必須確定建立者擁有相關的權限以及表格空間配額(Quota)。如果是將表格建立在自己的綱
要下,僅需要 CREATE TABLE 的系統權限以及足夠的表格空間配額即可。若要將表格建立在其他的綱
要下,則需要 CREATE ANY TABLE 的系統權限以及表格擁有者必須有足夠的表格空間配額。
21. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
綱要物件(Schema Object)的命名規則
所謂的綱要物件是指那些可以放在綱要中的物件,例如:表格、索引(Index)、視圖(View)、限制
(Constraint)、程序(Procedure)、函數(Function)、套件(Package)、觸發器(Trigger)等都是常見的綱要
物件。綱要其實是一個邏輯的容器,包含著屬於與綱要相同名字的使用者的所有物件的集合。因此當將一
個表格在 FRANK 的綱要下時,表示這個表格是屬於 FRANK 這個使用者擁有的物件。然而眾多的物件需
要許多不同的名字,以正確的辨別物件。因此需要一個命名規則來幫助資料庫管理人員記憶與方便管理這
些綱要物件。
物件名稱必須在 30 個 Bytes 之內。不過有少數例外,例如:資料庫名字只能有 8 個 Bytes,但是資料庫
連結(Database Link)可以多達 128 個 Bytes。
可以使用的字元僅限於 A 到 Z 或 a 到 z 的大小寫英文字母與 0 到 9 的阿拉伯數字,加上#、_、$這三
個特殊符號。若是資料庫連接還可以使用 . 與@兩種特殊字元。不過若在名字前後加上雙引號,則任何字
元都可使用,甚至空白也可以。
物件名稱不能使用資料庫的保留字,如 TABLE、INDEX 等。但若使用雙引號則不受此限。
物件名稱要以英文字母開頭,若使用雙引號不受此限。
物件名稱並沒有大小寫的差別,因為 Oracle 都會將物件名稱轉成大寫,才存到資料辭典。如果使用雙引
號則會保留原來的大小寫格式。
當使用雙引號包裹物件名稱時,未來使用該物件時,必須也要使用雙引號才能正確呼叫該物件。所以不建
議使用雙引號。
在同一個綱要下,以下幾種物件(TABLE/VIEW/SEQUENCE/Private
SYNONYM/PROCEDURE/FUNCTION/PACKAGE/MATERIALIZED VIEW/User-Defined Type)不能
使用相同的名稱,因為他們位於相同的命名空間(NameSpace)。但是不同綱要屬於不同的命名空間,所
以可以在 FRANK 綱要與 SCOTT 綱要中,都存在一個叫做 EMP 的表格。
以下的綱要物件(INDEX/CONSTRAINT/CLUSTER/TRIGGER/Private Database Link/DIMENSION)擁
有自己的命名空間,所以即便是同一個綱要中已有一個 EMP 表格,還是可以建立一個叫做 EMP 的索引
或限制。不過同一個命名空間中,不能有相同名稱的物件存在。
表格範例
當建立一個表格之前,首先要確定幾件事。
擁有 CREATE TABLE 權限或 CREATE ANY TABLE(將表格放到別人的綱要中)。
表格所在表格空間尚有足夠的配額。
此外還要決定表格所在的綱要、表格名稱、欄位名稱及欄位的資料型態與長度,而一個表格中最多可以
1000 個欄位。以下是建立堆集組織表格(Heap Organized Table)的範例。
SQL> CONNECT / AS SYSDBA
SQL> CREATE TABLE frank.t1 --schema.table_name,表示此表格要放在哪個綱要下。
2 (a NUMBER(4), --宣告欄位名稱與資料型態
3 b VARCHAR2(10),
4 c DATE DEFAULT SYSDATE) --設定欄位的預設值,預設值的資料型態必須與欄位相同。
5 ORGANIZATION HEAP --建立堆集組織表格,可以不用強調,因為堆疊表格為預設值。
6 TABLESPACE users; --此表格的區段(Segment)所放置的位置。
可以從DBA_TABLES/ALL_TABLES/USER_TABLES中查詢相關的資訊
SQL> SELECT owner,table_name,tablespace_name
2 FROM dba_tables
3 WHERE table_name='t1' and owner='frank';
no rows selected --表格名稱會轉換成大寫後,才存入資料辭典。
22. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> SELECT owner,table_name,tablespace_name
2 FROM dba_tables
3 WHERE table_name='T1' and OWNER='FRANK';
OWNER TABLE_NAME TABLESPACE_NAME
------------------------------ ----------------------------------- ------------------------------
FRANK T1 USERS
SQL> SELECT object_id,data_object_id,object_name,object_type
2 FROM dba_objects
3 WHERE owner='FRANK' and object_name='T1';
OBJECT_ID DATA_OBJECT_ID OBJECT_NAME OBJECT_TYPE
---------------- ------------------------- ----------------------- -------------------
84547 84547 T1 TABLE
/*在Oracle資料庫中每個物件都有一個獨一的物件ID(Object_Id),用來辨識物件,同時如果該物件是區段(se
gment)物件時,也會有一個資料物件ID(Data_Object_Id)用來辨識其所擁有的區段*/
SQL> SELECT segment_type,tablespace_name,header_file,header_block,bytes,blocks
2 FROM dba_segments
3 WHERE owner='FRANK' and segment_name='T1';
SEGMENT_TYPE TABLESPACE_NAME HEADER_FILE HEADER_BLOCK BYTES BLOCKS
----------------------- ------------------------------ -------------------- ------------------------- ------------- ---------------
TABLE USERS 4 889 65536 8
/*每個區段都可有DBA_SEGMENTS/ALL_SEGMENTS/USER_SEGMENTS中查知其相關的資訊,例如:S
EGMENT_TYPE可以得知該區段的型態為TABLE或INDEX等。 TABLESPACE_NAME則可以知道該區段所
使用的表格空間為何?而HEADER_FILE與HEADER_BLOCK則可以得到SEGMENT_HEADER所在的位
置。EXTENTS則顯示目前區段所被配置的擴充區段個數。BYTES與BLOCKS則顯示目前區段的大小,分別
以Bytes或Block個數呈現。*/
SQL> SELECT extent_id,file_id,block_id,blocks,bytes
2 FROM dba_extents
3 WHERE owner='FRANK' and segment_name='T1' and tablespace_name='USERS';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS BYTES
----------------- ------------- -------------- ------------- ----------
0 4 889 8 65536
/*此外也可以由DBA_EXTENTS/ALL_EXTENTS/USERS_EXTENTS中查知區段與擴充區塊的關係,例如:
EXTENT_ID表示該擴充區塊是區段中的第幾個擴充區塊。FILE_ID則表示該擴充區塊是由那個資料檔中取得
相關的資料區塊。BLOCK_ID表示該擴充區塊是由資料檔的第幾個資料區塊開始,BLOCKS則表示該擴充區
塊為幾個連續的資料區塊組成。*/
33. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
5 CONSTRAINT con_t1_b_uk UNIQUE(b)); --待所有欄位都宣告後,才宣告限制條件,此為表格層次
SQL> CREATE TABLE frank.con_t2
2 (a NUMBER CONSTRAINT con_t2_a_pk PRIMARY KEY,--使用Constraint參數設定限制條件的名字
3 b VARCHAR2(10),
4 c NUMBER NOT NULL, -- NOT NULL只能使用Column Level
5 CONSTRAINT con_t2_bc_uk UNIQUE(b,c));
--由多個欄位組成的限制條件,必須使用表格層次且必須加上欄位名稱。
SQL> CREATE TABLE frank.con_t3
2 (a NUMBER REFERENCES frank.con_t2(a));
--FOREIGN KEY在欄位層次與表格層次的宣告有些與不同
SQL> CREATE TABLE frank.con_t4
2 (a NUMBER,
3 b NUMBER,
4 CONSTRAINT con_t4_a_fk FOREIGN KEY(a) REFERENCES frank.con_t2(a),
5 CHECK(b>10)); --沒有指定限制條件名稱,將由Oracle伺服器自動指派。
/*可以透過DBA_CONSTRAINTS/ALL_CONSTRAINTS/USER_CONSTRIANTS查知限制條件的相關資訊*/
SQL> SELECT owner,table_name,constraint_name,constraint_type
2 FROM dba_constraints
3 WHERE table_name LIKE 'CON_T%'
4 ORDER BY table_name;
OWNER TABLE_NAME CONSTRAINT_NAME C
------------ ---------------------- ------------------------------ ---
FRANK CON_T1 CON_T1_B_UK U --U表示為UNIQUE KEY
FRANK CON_T1 SYS_C0014764 P --P表示為PRIMARY KEY
FRANK CON_T2 CON_T2_BC_UK U
FRANK CON_T2 SYS_C0014766 C --C可能是CHECK或NOT NULL
FRANK CON_T2 CON_T2_A_PK P
FRANK CON_T3 SYS_C0014769 R --R是FOREIGN KEY
FRANK CON_T4 SYS_C0014770 C
FRANK CON_T4 CON_T4_A_FK R
SQL> SELECT constraint_name,constraint_type,search_condition
2 FROM dba_constraints
3 WHERE table_name LIKE 'CON_T%' AND constraint_type='C';
CONSTRAINT_NAME C SEARCH_CONDITION
------------------------------ --- ------------------------------
SYS_C0014766 C "C" IS NOT NULL --依search_condition來區分CHECK與NOT NULL
SYS_C0014770 C b>10 --這個限制條件為CHECK
34. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
ON DELETE 參數
在宣告 FOREIGN KEY 時,必須指定當父表格資料被刪除,子表格資料要如何處理。共分成以下三種方
式:
NO DELETE ACTION:當宣告外鍵時,沒有設定 ON DELETE 參數。表示若父表格的資料列被刪除或
PRIMARY KEY/UNIQUE 限制所在的欄位值被異動時,如果子表格中有任何 FOREIGN KEY 限制的欄位
值受到影響導致違反 FOREIGN KEY 限制條件,則對父表格的刪除或異動將出現錯誤以及自動倒回該操
作。只有被刪除或異動的欄位值沒有被任何 FOREIGN KEY 參考時,才能順利地被刪除或異動。
SQL> CONNCT / AS SYSDBA
SQL> CREATE TABLE frank.p_table --父表格
2 (a NUMBER CONSTRAINT p_tab_a_pk PRIMARY KEY,
3 b VARCHAR2(10));
SQL> CREATE TABLE frank.c_table --子表格
2 (a VARCHAR2(10),
35. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
3 b NUMBER CONSTRAINT c_tab_b_fk REFERENCES frank.p_table(a)); --沒有ON DELETE設定
--建立父表格的測試資料
SQL> INSERT INTO frank.p_table VALUES(1,'A');
SQL> INSERT INTO frank.p_table VALUES(2,'B');
SQL> INSERT INTO frank.p_table VALUES(3,'C');
SQL> COMMIT;
SQL> SELECT * FROM frank.p_table;
A B
---------- ----------
1 A
2 B
3 C
--建立子表格測試資料
SQL> INSERT INTO frank.c_table VALUES('A',1); --新增成功,因為P_TABLE的A欄位中有1這個值
SQL> INSERT INTO frank.c_table VALUES('B',10);
INSERT INTO frank.c_table VALUES('B',10)
*
ERROR at line 1:
ORA-02291: integrity constraint (FRANK.C_TAB_B_FK) violated - parent key not found
--新增失敗,因為P_TABLE的A欄位中並沒有10這個值
SQL> INSERT INTO frank.c_table VALUES('B',3);
SQL> COMMIT;
SQL> SELECT * FROM frank.c_table;
A B
---------- -----------
A 1
B 3
SQL> SELECT table_name,constraint_name,constraint_type,delete_rule,r_constraint_name
2 FROM dba_constraints
3 WHERE table_name IN ('P_TABLE','C_TABLE');
TABLE_NAME CONSTRAINT_NAME C DELETE_RU R_CONSTRAINT_NAME
------------------------------ ------------------------------ -- ---------------- ------------------------------
C_TABLE C_TAB_B_FK R NO ACTION P_TAB_A_PK
P_TABLE P_TAB_A_PK P
/* 當CONSTRAINT_TYPE為R時,R_OWNER/R_CONSTRAINT_NAME為這個外鍵所參考的限制名稱與限
制擁有者。由以上結果可以得知,C_TABLE的C_TAB_B_FK參考P_TABLE上的P_TAB_A_PK主鍵限制。同
時當父表格被刪除資料或異動主鍵值時,子表格不進行任何動作(DELETE_RULE為NO ACTION)*/
SQL> DELETE frank.p_table WHERE a=2;
1 row deleted. --因為沒有任何子表格的外鍵參考這個欄位值,所以父表格的刪除動作成功完成。
SQL> SELECT * FROM frank.p_table;
A B
---------- ----------
1 A
3 C
SQL> DELETE frank.p_table WHERE a=1;
DELETE frank.p_table WHERE a=1
*
36. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
ERROR at line 1:
ORA-02292: integrity constraint (FRANK.C_TAB_B_FK) violated - child record found
/*因為有至少一個子表格參考這筆資料列中的A欄位值*/
SQL> UPDATE frank.p_table SET a=2 WHERE a=3;
UPDATE frank.p_table SET a=2 WHERE a=3
*
ERROR at line 1:
ORA-02292: integrity constraint (FRANK.C_TAB_B_FK) violated - child record found
/*因為子表格中尚有欄位值參考這筆資料列中的A欄位值*/
SQL> DROP TABLE frank.p_table;
DROP TABLE frank.p_table
*
ERROR at line 1:
ORA-02449: unique/primary keys in table referenced by foreign keys
/*即便是刪除父表格時,也會受到外鍵的影響。必須先刪除子表格或使用CASCADE CONSTRAINTS的選
項,方可以順利刪除父表格*/
SQL> DROP TABLE frank.p_table CASCADE CONSTRAINTS;
Table dropped.
/*CASCADE CONSTRAINTS選項會將所有參考此父表格的所有外鍵限制刪除,但不會將子表格一起刪除*/
ON DELETE CASCASE:當設定此種選項後,父表格的資料列被刪除,相對的子表格資料列同時也會被
刪除。但若只有異動 PRIMRARY KEY 或 UNIQUE KEY 的欄位值,還是會檢查是否有子表格的欄位參考
這些欄位值,如果有被參考,依然會出現違反 Reference Integrity 的錯誤訊息。
SQL> CREATE TABLE frank.p_table --父表格
2 (a NUMBER CONSTRAINT p_tab_a_pk PRIMARY KEY,
3 b VARCHAR2(10));
SQL> CREATE TABLE frank.c_table --子表格
2 (a VARCHAR2(10),
3 b NUMBER CONSTRAINT c_tab_b_fk REFERENCES frank.p_table(a)
4 ON DELETE CASACDE);
--建立父表格的測試資料
SQL> INSERT INTO frank.p_table VALUES(1,'A');
SQL> INSERT INTO frank.p_table VALUES(2,'B');
SQL> INSERT INTO frank.p_table VALUES(3,'C');
SQL> COMMIT;
SQL> SELECT * FROM frank.p_table;
A B
---------- ----------
1 A
2 B
3 C
--建立子表格測試資料
SQL> INSERT INTO frank.c_table VALUES('A',1);
SQL> INSERT INTO frank.c_table VALUES('B',3);
SQL> COMMIT;
SQL> SELECT * FROM frank.c_table;
A B
---------- -----------
37. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
A 1
B 3
SQL> SELECT table_name,constraint_name,constraint_type,delete_rule,r_constraint_name
2 FROM dba_constraints
3 WHERE table_name IN ('P_TABLE','C_TABLE');
TABLE_NAME CONSTRAINT_NAME C DELETE_RU R_CONSTRAINT_NAME
------------------------------ ------------------------------ -- ---------------- ------------------------------
C_TABLE C_TAB_B_FK R CASCADE P_TAB_A_PK
P_TABLE P_TAB_A_PK P
--當DELETE_RULE為CASCADE,表示FOREIGN KEY的選項為ON DELETE CASCADE
SQL> DELETE frank.p_table WHERE a=1;
1 row deleted.
SQL> SELECT * FROM frank.p_table; --父表格
A B
---------- ----------
2 B
3 C
SQL> SELECT * FROM frank.c_table; --子表格中所有B欄位為1的資料列
A B
---------- -----------
B 3
SQL> UPDATE frank.p_table SET a=10 WHERE b='C';
UPDATE frank.p_table SET a=10 WHERE b='C'
*
ERROR at line 1:
ORA-02292: integrity constraint (FRANK.C_TAB_FK) violated - child record found
/*因為子表格中尚有欄位值參考這筆資料列中的A欄位值*/
ON DELETE SET NULL: 當設定此種選項後,父表格的資料列被刪除,相對的子表格資料列雖不會被
刪除,但 FOREIGN KEY 限制條件的欄位值將會被更改為 NULL(空值)。但若只有異動 PRIMRARY KEY
或 UNIQUE KEY 的欄位值,還是會檢查是否有子表格的欄位參考這些欄位值,如果有被參考,依然會出
現違反 Reference Integrity 的錯誤訊息。
SQL> CREATE TABLE frank.p_table --父表格
2 (a NUMBER CONSTRAINT p_tab_a_pk PRIMARY KEY,
3 b VARCHAR2(10));
SQL> CREATE TABLE frank.c_table --子表格
2 (a VARCHAR2(10),
3 b NUMBER CONSTRAINT c_tab_b_fk REFERENCES frank.p_table(a)
4 ON DELETE SET NULL);
--建立父表格的測試資料
SQL> INSERT INTO frank.p_table VALUES(1,'A');
SQL> INSERT INTO frank.p_table VALUES(2,'B');
SQL> INSERT INTO frank.p_table VALUES(3,'C');
38. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> COMMIT;
SQL> SELECT * FROM frank.p_table;
A B
---------- ----------
1 A
2 B
3 C
--建立子表格測試資料
SQL> INSERT INTO frank.c_table VALUES('A',1);
SQL> INSERT INTO frank.c_table VALUES('B',3);
SQL> COMMIT;
SQL> SELECT * FROM frank.c_table;
A B
---------- -----------
A 1
B 3
SQL> SELECT table_name,constraint_name,constraint_type,delete_rule,r_constraint_name
2 FROM dba_constraints
3 WHERE table_name IN ('P_TABLE','C_TABLE');
TABLE_NAME CONSTRAINT_NAME C DELETE_RU R_CONSTRAINT_NAME
------------------------------ ------------------------------ -- ---------------- ------------------------------
C_TABLE C_TAB_B_FK R SET NULL P_TAB_A_PK
P_TABLE P_TAB_A_PK P
--當DELETE_RULE為SET NULL,表示FOREIGN KEY的選項為ON DELETE SET NULL
SQL> DELETE frank.p_table WHERE a=1;
1 row deleted.
SQL> SELECT * FROM frank.p_table;
A B
---------- ----------
2 B
3 C
SQL> SELECT * FROM frank.c_table; --子表格中受影響的外鍵欄位值被改為NULL,但未被刪除
A B
---------- -----------
A
B 3
SQL> UPDATE frank.p_table SET a=10 WHERE b='C';
UPDATE frank.p_table SET a=10 WHERE b='C'
*
ERROR at line 1:
ORA-02292: integrity constraint (FRANK.C_TAB_FK) violated - child record found
外鍵欄位上的索引
由之前的範例與說明得知,有設定 ON DELETE CASCASE 或 ON DELETE SET NULL 時,父表格的
刪除會導致子表格的資料列被刪除或欄位值改為 NULL(空值),這時不光是父表格需要一些鎖定(Table
39. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
Level 的 Row Exclusive 及 Row Level 的 Exclusive) ,同時子表格上也需要一些鎖定(Lock)。至於需要
何種型態的子表格鎖定,根據 Foreign Key 限制條件所在的欄位上是否有索引存在而有所不同。如果
Foreign Key 的欄位上沒有索引存在,則在任何 ON DELETE 設定下,都會先取得子表格(Table Level
的 Share Row Exclusive)。然而在握有子表格 Share Row Exclusive 的期間,將會禁止其他 Session 對
子表格進行任何 DML 與 DLL 操作,直到釋放 Share Row Exclusive 為止。不過自 Oracle9i 後,當完成
對子表格刪除或異動後,立刻就降為 Row Exclusive(不會禁止其他 Session 對子表格的 DML,僅禁止
DDL 操作),所以整體的鎖定競爭已經比 Oracle9i 之前的版本少,但是若同時有多個 Session 對父表格
進行 DML 操作,這時子表格還有可能會有嚴重的鎖定競爭發生。
然而若在子表格的 Foreign Key 欄位上建立一個索引,對子表格的鎖定則直接就是 Row Exclusive(僅禁
止 DDL),不需要先取得 Share Row Exclusive,如此 ON DELETE 參數所造成的子表格鎖定競爭將大
幅減少。
--以SYSDBA身份登入
SQL> CONNECT / AS SYSDBA
SQL> SELECT sid,type,id1,id2,lmode,request FROM v$lock WHERE TYPE IN ('TX','TM');
no rows selected --目前整個資料庫中,暫時還沒有Table Level與Row Level的Lock發生
SQL> lock table frank.c_table in exclusive mode;
/*人工取得對子表格的Table Level Lock,exclusive mode為獨佔模式,不允許其他Session同時握有任何型
態的Table Level Lock在子表格上。這個動作只是為了解Parent Table與Child Table間的鎖定關係而做,正
式環境中盡量不要使用人工要求鎖定的操作,以免影響正常的鎖定行為。*/
SQL> SELECT sid,type,id1,id2,lmode,request FROM v$lock WHERE TYPE IN ('TX','TM');
SID TY ID1 ID2 LMODE REQUEST
----------- ---- -------------- ----------- ------------ --------------
253 TM 84747 0 6 0
SQL> SELECT owner,object_name,object_type FROM dba_objects WHERE object_id=84747;
OWNER OBJECT_NAME OBJECT_TYPE
------------------------------ ----------------------------------- ----------------------
FRANK C_TABLE TABLE
/*當LOCK TYPE為TM時,ID1為該物件的Object_ID*/
--開啟另一個Session
SQL> SELECT * FROM frank.p_table; --p_table為Parent Table
A B
---------- ----------
2 B
3 C
1 A
SQL> SELECT * FROM frank.c_table; --c_table為Child Table,Foreign Key為ON DEELET SET NULL
A B
---------- ----------
A 1
B 3
SQL> DELETE frank.p_table WHERE a=1;
/*因為frank.c_table已經被其他人握有Table Level:Exclusive Lock,所以現在正在等待frank.c_table上的L
ock*/
回到SYS的Session
SQL> select sid,type,id1,id2,lmode,request from v$lock where type in ('TX','TM');
40. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SID TY ID1 ID2 LMODE REQUEST
----------- ---- -------------- ---------- ----------- --------------
238 TM 84745 0 3 0 --DML所取得的Row Exclusive
238 TM 84747 0 0 5 --想要取得的TM:Share Row Exclusive
253 TM 84747 0 6 0 --人工取得的TM:Exclusive
/*因為Child Table上已經有人取得Exclusive Lock,所以ON DELETE想要的Share Row Exclusive必須等
待,此外對Parent Table的DML操作已取得Row Exclusive。*/
SQL> SELECT owner,object_name,object_type FROM dba_objects WHERE object_id=84745;
OWNER OBJECT_NAME OBJECT_TYPE
------------------------------ ----------------------------------- ----------------------
FRANK P_TABLE TABLE
SQL> COMMIT; --釋放Child Table上的Exclusive Lock
SQL> SELECT sid,type,id1,id2,lmode,request FROM v$lock WHERE TYPE IN ('TX','TM');
SID TY ID1 ID2 LMODE REQUEST
----------- ---- -------------- ---------- ----------- --------------
238 TM 84745 0 3 0
238 TM 84747 0 3 0 --當取得SRX開始進行DML,之後立刻降級為RX
238 TX 524316 12486 6 0 --取得Table Lock後,接著取得Row Lock
8.4.2 限制條件的操作
新增或刪除限制條件
所有的限制條件都可以隨時新增或刪除,除了 NOT NULL 必須使用 ALTER TABLE MODIFY 的語法外,
其餘的限制條件可以使用表格層次的方法新增限制或 ALTER TABLE MODIFY 的語法新增限制。
SQL> CREATE TABLE frank.t1
2 (a NUMBER,
3 b VARCHAR2(10));
SQL> DESC frank.t1
Name Null? Type
----------------------------------------------------- -------------- ----------------------------------------
A NUMBER
B VARCHAR2(10)
SQL> ALTER TABLE frank.t1
2 MODIFY (a NUMBER CONSTRAINT t1_a_nn NOT NULL); --NOT NULL必須使用MODIFY
SQL> DESC frank.t1
Name Null? Type
----------------------------------------------------- -------------- ----------------------------------------
A NOT NULL NUMBER
B VARCHAR2(10)
SQL> ALTER TABLE frank.t1
2 ADD CONSTRAINT t1_a_pk PRIMARY KEY(a); --新增一個PRIMARY KEY限制條件
SQL> ALTER TABLE frank.t1
2 DROP CONSTRAINT t1_a_pk CASCADE;
/*刪除一個限制時,若該限制為PRIMARY KEY或UNIQUE KEY要加上CASCADE ,同時將子表格的FOREI
GN KEY限制條件刪除*/
41. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
8.4.3 限制條件的狀態
限制條件的狀態影響限制對新增資料的檢查與否以及對現存資料的驗證,其中根據 DISABLE/ENABLE
與 NOVALIDATE/VALIDATE 組成四種情況:
DISBALE NOVALIDATE:當限制條件的狀態為 DISABLE NOVALIDATE 時,表示雖然表格上有設定
限制條件,但是並不會對新增的資料列進行檢查,同時也不會驗證表格中已經存在資料列是否遵守限制。
也就是說限制條件只是一個宣告,但完全不會發生任何功能。
ENABLE NOVALIDATE:當限制條件的狀態為 ENABLE NOVALIDATE 時,表示新增的資料列必須遵
守限制條件的規範,才能新增到表格中,不過已經存在表格中的資料列並不一定要遵守限制條件的規範。
所以現在的表格中的資料,可能還有一些資料是違反限制條件的規範,但至少從現在開始新增的資料一定
會遵守限制條件的規範。
ENABLE VALIDATE:當限制條件的狀態為 ENABLE VALIDATE 時,表示新增的資料列必須遵守限制
條件的規範,才能新增到表格中,同時也驗證已經存在表格中的資料也都遵守限制條件的規範。這種狀態
是宣告限制條件的預設狀態。
DISABLE VALIDATE:當限制條件的狀態為 DISABLE VALIDATE 時,表示新增的資料列不須遵守限
制條件的規範,也可以新增到表格中,不過所以已經存在的資料一定遵守限制條件的規範。這樣的狀態有
一點自相矛盾,因為如果一筆違反限制條件的資料列成功新增到表格中,那麼要如何達到已經存在的資料
列一定遵守限制條件呢?最簡單的方法是禁止這個表格的 DML 操作,就不會有這種問題發生。所以當狀
態為 DISABLE VALIDATE 時,限制所在的表格是不允許被新增、異動或刪除。這種狀態是用在當表格
與分區表格(Partition Table)的區段(Segment)進行交換操作時,所有的限制條件都必須是此種狀態,才
能順利進行區段的交換。
SQL> DROP TABLE frank.t1; --刪除frank.t1表格
SQL> CREATE TABLE frank.t1
2 (a NUMBER CONSTRAINT t1_a_ck CHECK(a>=10),
3 b VARCHAR2(10));
SQL> SELECT status,validated FROM dba_constraints
2 WHERE owner='FRANK' AND table_name='T1' AND constraint_name='T1_A_CK';
STATUS VALIDATED
------------------ -----------------
ENABLED VALIDATED --宣告限制條件的預設狀態
SQL> ALTER TABLE frank.t1 DISABLE NOVALIDATE CONSTRAINT t1_a_ck;
/*如果只有DISABLE表示為DISABLE NOVALIDATE。若限制條件為PRIMARY KEY或UNIQUE,需要加上C
ASCADE將子表格上FOREIGN KEY的狀態也改為DISABLE*/
SQL> SELECT status,validated FROM dba_constraints
2 WHERE owner='FRANK' AND table_name='T1' AND constraint_name='T1_A_CK';
STATUS VALIDATED
------------------ ------------------------
DISABLED NOT VALIDATED
SQL> INSERT INTO frank.t1 VALUES(10,'A');
SQL> INSERT INTO frank.t1 VALUES(1,'B'); --1小於10,但依然可以成功新增,因為狀態為DISABLED
SQL> COMMIT;
SQL> SELECT * FROM frank.t1;
A B
------------ ----------
10 A
42. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
1 B
SQL> ALTER TABLE frank.t1 ENABLE VALIDATE CONSTRAINT t1_a_ck;
ALTER TABLE frank.t1 ENABLE VALIDATE CONSTRAINT t1_a_ck
*
ERROR at line 1:
ORA-02293: cannot validate (FRANK.T1_A_CK) - check constraint violated
/*已經存在的資料列中,有些違反限制條件的規範,所以不能將狀態設為VALIDATE。必須自行將違反限制條
件的資料進行清理後,才能將狀態設為VALIDATE*/
SQL> ALTER TABLE frank.t1 ENABLE NOVALIDATE CONSTRAINT t1_a_ck;
/*退而求其次地將狀態條件先設定為ENABLE NOVALIDATE,避免有更多違反限制條件的資料列被新增,之
後再將違反限制條件的資料列修正,再將狀態改為ENABLE VALIDATE即可。如果只有ENABLE則表示為EN
ABLE VALIDATE*/
SQL> SELECT status,validated FROM dba_constraints
2 WHERE owner='FRANK' AND table_name='T1' AND constraint_name='T1_A_CK';
STATUS VALIDATED
------------------ ------------------------
ENABLED NOT VALIDATED
SQL> INSERT INTO frank.t1 VALUES(2,'C'); --2比10小,所以無法新增
INSERT INTO frank.t1 VALUES(2,'C')
*
ERROR at line 1:
ORA-02290: check constraint (FRANK.T1_A_CK) violated
SQL> INSERT INTO frank.t1 VALUES(20,'C');
SQL> COMMIT;
SQL> SELECT * FROM frank.t1;
A B
------------ ----------
10 A
1 B
20 C
找出那些資料列違反限制條件的規範,首先必須先建立一個表格 EXCEPTIONS,這個表格利用
$ORACLE_HOME/rdbms/admin/utlexpt1.sql 產生。再利用 ALTER TABLE EXCEPTIONS 語法將違
反某個限制條件的資料列 ROWID 填入 exceptions 表格,如此便可以得知哪些資料列違反限制條件。
SQL> @$ORACLE_HOME/rdbms/admin/utlexpt1.sql
SQL> DESC exceptions
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
ROW_ID ROWID
OWNER VARCHAR2(30)
TABLE_NAME VARCHAR2(30)
CONSTRAINT VARCHAR2(30)
SQL> SELECT row_id FROM exceptions
2 WHERE owner='FRANK' AND table_name='T1' AND constraint='T1_A_CK';
43. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
no rows selected
SQL> ALTER TABLE frank.t1 ENABLE VALIDATE CONSTRAINT t1_a_ck
2 EXCEPTIONS INTO exceptions; --將違反t1_a_ck的資料列ROWID新增到exceptions表格中
ALTER TABLE frank.t1 ENABLE VALIDATE CONSTRAINT t1_a_ck
*
ERROR at line 1:
ORA-02293: cannot validate (FRANK.T1_A_CK) - check constraint violated
SQL> SELECT row_id FROM exceptions
2 WHERE owner='FRANK' AND table_name='T1' AND constraint='T1_A_CK';
ROW_ID
--------------------------------------------------------------------------------------------------------------
AAAUsOAAEAAAAP6AAB --這筆ROW違反t1_a_ck
SQL> SELECT search_condition FROM dba_constraints
2 WHERE constraint_name='T1_A_CK' AND table_name='T1' AND owner='FRANK';
SEARCH_CONDITION
--------------------------------------------------------------------------------
a>=10
SQL> SELECT * FROM frank.t1
2 WHERE rowid IN (SELECT row_id FROM exceptions
3 WHERE owner='FRANK' AND table_name='T1' AND constraint='T1_A_CK');
A B
----------- ----------
1 B
/*修正違反t1_a_ck的資料列,讓它遵守t1_a_ck的規範*/
SQL> UPDATE frank.t1 SET a=11 WHERE rowid='AAAUsOAAEAAAAP6AAB';
1 row updated.
SQL> SELECT * FROM frank.t1 WHERE rowid='AAAUsOAAEAAAAP6AAB';
A B
----------- ----------
11 B
SQL> DELETE exceptions WHERE owner='FRANK' AND table_name='T1' AND constraint='T1_A_CK';
/*清除exceptions表格中已經處理過的資料列*/
SQL> COMMIT;
SQL> SELECT * FROM frank.t1;
A B
----------- ----------
10 A
11 B
20 C
SQL> ALTER TABLE frank.t1 ENABLE VALIDATE CONSTRAINTS t1_a_ck;
SQL> SELECT status,validated FROM dba_constraints
44. Chapter 8 Table
Oracle Database 11g 資料庫管理入門
2 WHERE owner='FRANK' AND table_name='T1' AND constraint_name='T1_A_CK';
STATUS VALIDATED
------------------ -----------------
ENABLED VALIDATED
/*未來新增的資料必須遵守限制條件,同時已經存在的資料列也經過驗證確定遵守限制條件的規範*/
SQL> ALTER TABLE frank.t1 DISABLE VALIDATE CONSTRAINT t1_a_ck;
SQL> SELECT status,validated FROM dba_constraints
2 WHERE owner='FRANK' AND table_name='T1' AND constraint_name='T1_A_CK';
STATUS VALIDATED
------------------ -----------------
DISABLED VALIDATED --此種狀態下,表格不允許DML操作
SQL> INSERT INTO frank.t1 VALUES(40,'D');
INSERT INTO frank.t1 VALUES(40,'D')
*
ERROR at line 1:
ORA-25128: No insert/update/delete on table with constraint (FRANK.T1_A_CK) disabled and validate
d
8.4.4 不可延緩與可延緩的限制條件
當表格的限制條件狀態為 ENABLE 時,新增的資料列必須遵守限制條件的規範才能被新增。然而何時進
行這個檢查,分成 DML 指令結束(IMMEDIATE)或交易結束(DEFERRED)兩種。
NOT DEFERRABLE(不可延緩)
然而當建立限制條件時,如果沒有明確指定限制條件可以延緩(DEFERRABLE),則限制條件的狀態就只
能為不可延緩(NOT DEFERRABLE)。而且事後也不可以更改,必須刪除現存的限制條件,重新建立一個
可以延緩的限制條件。如此該限制條件只能在 DML 指令結束後,立刻檢查該 DML 指令的結果是否遵守
限制條件的規範,如果違反限制條件的規範,該 DML 指令的結果將立刻被倒回(ROLLBACK),但是整個
交易(Transaction)依然存在不會倒回。
SQL> CREATE TABLE frank.t2
2 (a NUMBER CONSTRAINT t2_a_pk PRIMARY KEY, --此限制條件預設為NOT DEFERRABLE
3 b VARCHAR2(10));
SQL> SELECT deferrable,deferred
2 FROM dba_constraints
3 WHERE owner='FRANK' AND table_name='T2' AND constraint_name='T2_A_PK';
DEFERRABLE DEFERRED
------------------------------- ----------------
NOT DEFERRABLE IMMEDIATE
SQL> INSERT INTO frank.t2 VALUES(1,'A');
SQL> INSERT INTO frank.t2 VALUES(2,'B');
SQL> INSERT INTO frank.t2 VALUES(1,'C');
INSERT INTO frank.t2 VALUES(1,'C')
*
ERROR at line 1:
ORA-00001: unique constraint (FRANK.T2_A_PK) violated
/*此INSERT違反PRIMARY KEY,因此該INSERT的結果被倒回(ROLLBACK),但整個交易並未倒回或確認
(COMMIT)*/
SQL> SELECT * FROM frank.t2; --並未倒回整個交易