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
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> CREATE TABLEfrank.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 PARAMETERuser_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 Database11g 中,被當作一般的欄位,所以除了不能直接新增、異動其內容外,其
餘的操作如:當做主鍵、建立索引等都可以正常使用。
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
2FROM 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則表示該擴充區
塊為幾個連續的資料區塊組成。*/
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
5 CONSTRAINT con_t1_b_ukUNIQUE(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 NUMBERCONSTRAINT 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 line1:
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 的 RowExclusive 及 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 ID1ID2 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> ALTERTABLE 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; --並未倒回整個交易
45.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
A B
---------- ----------
1A
2 B
SQL> INSERT INTO frank.t2 VALUES(3,'C');
SQL> COMMIT; --這時交易才結束
SQL> SELECT * FROM frank.t2;
A B
---------- ----------
1 A
2 B
3 C
DEFERRABLE(可延緩)
當限制條件設定為可以延緩(DEFERRABLE)時,還必須設定限制條件預定的檢查時間點為起始立刻
(INITIALLY IMMEDIATE)或起始延緩(INITIALLY DEFERRED)。
DEFERRABLE INITIALLY IMMEDIATE:當限制條件為可以延緩但起始立刻時,限制條件的檢查如同不
可延緩的限制條件一樣,DML 指令完成後立刻檢查是否違反限制條件的規範,若發現違反限制條件則立
刻倒回該 DML 的操作,但整個交易(Transaction)依然繼續中。不過既然是 INITIALLY IMMEDIATE(起
始立刻),表示可以事後更改其檢查時點為交易確認(COMMIT)。可以透過 SQL 指令將限制條件設定為延
緩檢查,不過透過 DBA_CONSTRAINTS 的 DEFERRED 欄位值並無法看出這項改變,因為 DEFERRED
欄位值是預設值。而每個 SESSION 可以依需要而自行調整檢查的時點。
DEFERRABLE INITIALLY DEFERRED: 當限制條件為可以延緩且起始延緩時,限制條件的檢查不會在
DML 指令完成後,立刻檢查是否違反限制條件的規範。而是等到交易確認時,才檢查整個交易中的異動
結果,是否違反限制條件。如果違反限制條件的規範,則倒回(ROLLBACK)整個交易。雖然預設的選項
是起始延緩,但是可以透過 SQL 指令將限制條件改為 DML 指令結束後立刻檢查。
SQL> CREATE TABLE frank.t3
2 (a NUMBER CONSTRAINT t3_a_pk PRIMARY KEY DEFERRABLE INITIALLY IMMEDIATE,
3 b NUMBER CONSTRAINT t3_b_ck CHECK(b>=10) DEFERRABLE INITIALLY DEFERRED);
SQL> SELECT constraint_name,deferrable,deferred
2 FROM dba_constraints
3 WHERE owner='FRANK' AND table_name='T3';
CONSTRAINT_NAME DEFERRABLE DEFERRED
------------------------------ ----------------------- ----------------
T3_B_CK DEFERRABLE DEFERRED
T3_A_PK DEFERRABLE IMMEDIATE
SQL> INSERT INTO frank.t3 VALUES(1,10);
SQL> INSERT INTO frank.t3 vALUES(2,2);
INTO frank.t3 VALUES(1,30);
INSERT INTO frank.t3 VALUES(1,30)
*
ERROR at line 1:
ORA-00001: unique constraint (FRANK.T3_A_PK) violated
/*因為限制條件T3_A_PK是可延緩但起始立刻,所以每個DML操作後,會立刻進行檢查。如果有違反限制條
件的情況發生,則倒回此DML操作,但不影響正在進行的交易。*/
SQL> SELECT * FROM frank.t3;
A B
46.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
----------- --------------
1 10
22
SQL> INSERT INTO frank.t3 VALUES(4,40);
SQL> SELECT * FROM frank.t3;
A B
----------- --------------
1 10
2 2
4 40
SQL> COMMIT;
COMMIT
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02290: check constraint (FRANK.T3_B_CK) violated
/*當確認交易時,發現交易中有些異動違反限制條件T3_B_CK(可延緩且起始延緩的限制條件),但因為不是每
個DML結束後立刻檢查,因此無法得知是那個DML造成的,所以只好將整個交易倒回(ROLLBACK)。*/
SQL> SELECT * FROM frank.t3;
no rows selected
變更檢查的時點
SESSION LEVEL:當使用 ALTER SESSION SET 指令時,這個設定會持續到整個 SESSION 的生命週
期,除非遇到另一個 ALTER SESSION SET 的不同設定。
SQL> ALTER SESSION SET CONSTRAINTS=DEFERRED;
/*從現在開始所有可延緩的限制條件,都變成交易確認時才檢查是否違反限制條件。*/
SQL> ALTER SESSION SET CONSTRAINTS=IMMEDIATE;
/*從現在開始所有可延緩的限制條件,不管其預設值為INITALLY IMMEDATE或INITIALLY DEFERRED,都
變成DML指令結束後立刻檢查是否違反限制條件。*/
SQL> ALTER SESSION SET CONSTRAINTS=DEFAULT;
/*從現在開始所有可延緩的限制條件,全部都依據其預設值的設定決定檢查時點。*/
TRANSACTION LEVEL:如果不需要在整個 SESSION 中延續檢查時點的改變效果,可以使用 SET
CONSTAINTS 的指令,在交易中改變某個或所有限制條件的檢查時點。但是當交易結束後,限制條件的
檢查時點便回復交易前的設定。不過 SET CONSTRAINTS 不能用在 TRIGGER 程式中。
SQL> SET CONSTRAINTS frank.t3_b_ck DERERRED;
/*將t3_b_ck在目前的交易中,設定為延緩檢查。*/
SQL> SET CONSTRAINTS frank.t3_b_ck IMMEDIATE;
/*將t3_b_ck在目前的交易中,設定為立刻檢查。*/
SQL> SET CONSTRAINTS frank.t3_a_pk,frank.t3_b_ck IMMEDIATE;
/*將t3_a_pk及t3_b_ck在目前的交易中,設定為立刻檢查。*/
SQL> SET CONSTRAINTS ALL IMMEDIATE; --ALL代表所有的限制條件
SQL> SET CONSTRAINTS ALL DEFERRED;
47.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> ALTER SESSIONSET CONSTRAINTS=DEFERRED; --SESSION LEVEL
SQL> desc frank.t3
Name Null? Type
----------------------------------------------------------- -------- ----------------------------------------
A NUMBER
B NUMBER
SQL> SELECT * FROM frank.t3;
no rows selected
SQL> INSERT INTO frank.t3 VALUES(1,1);
SQL> INSERT INTO frank.t3 VALUES(2,20);
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02290: check constraint (FRANK.T3_B_CK) violated
SQL> SET CONSTRAINTS t3_b_ck immediate; --TRANSACTION LEVEL
Constraint set.
SQL> INSERT INTO frank.t3 VALUES(1,1); --TRANSACTION開始
insert into frank.t3 values(1,1)
*
ERROR at line 1:
ORA-02290: check constraint (FRANK.T3_B_CK) violated
/* DML指令結束後立刻檢查,不管SESSION LEVEL的設定。*/
SQL> SELECT * FROM frank.t3;
no rows selected
SQL> ROLLBACK; --結束目前的交易
Rollback complete.
SQL> INSERT INTO frank.t3 VALUES(1,1); --回歸SESSION LEVEL的設定,延緩所有可延緩的限制條件
1 row created.
SQL> SELECT * FROM frank.t3;
A B
----------- -----------
1 1
SQL> COMMIT;
COMMIT
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02290: check constraint (FRANK.T3_B_CK) violated
8.4.5 限制條件與索引
當啟用(ENABLE)一個表格的 PRIMARY KEY 或 UNIQUE KEY 限制條件前,會先確認在 PRIMARY KEY
或 UNIQUE KEY 的欄位上,是否已經有索引(INDEX)存在。如果沒有任何索引存在,則會自動依限制條
48.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
件的可延緩(DEFERRABLE)設定,來建立唯一值索引(UNIQUE INDEX)或非唯一值索引(NON-UNIQUE
INDEX)。而且索引名稱預設與限制條件相同,除非使用 USINGINDEX 的設定。NOT DEFERRABLE(不
可延緩)的 PRIMARY KEY 或 UNIQUE INDEX 將建立為 UNIQUE INDEX,而 DEFERRABLE(可延緩)的
PRIMARY KEY 或 UNIQUE KEY 則建立為 NON-UNIQUE INDEX。
相對地,當將 PRIMARY KEY 或 UNIQUE KEY 限制條件失效(DISABLE)時,則會將那些因為啟用
PRIMARY KEY 或 UNIQUE KEY 限制條件而自動建立的 UNIQUE INDEX,預設自動隨著限制條件的失
效而被刪除。不過需要特別注意的是,NON-UNIQUE INDEX 預設不會被刪除的。
SQL> CREATE TABLE frank.t4
2 (a NUMBER CONSTRAINT t4_a_pk PRIMARY KEY DEFERRABLE,
3 b NUMBER CONSTRAINT t4_b_uk UNIQUE,
4 c NUMBER CHECK(c>=10)
5 );
SQL> SELECT constraint_name,constraint_type,deferrable,status,index_name
2 FROM dba_constraints
3 WHERE owner='FRANK' AND table_name='T4';
CONSTRAINT_NAME C DEFERRABLE STATUS INDEX_NAME
------------------------------- -- ------------------------------- ----------------------- -------------------
SYS_C0017756 C NOT DEFERRABLE ENABLED
T4_A_PK P DEFERRABLE ENABLED T4_A_PK
T4_B_UK U NOT DEFERRABLE ENABLED T4_B_IDX
/*T4_A_PK為PRIMARY KEY且沒有設定不同的索引名字,所以索引名字與限制條件相同。不過因為限制條
件與索引不在同一個NAMESPACE中,所以可以有相同的名字即便在同一個SCHEMA(綱要)中。
T4_B_PK為UNIQUE KEY,所以也必須有索引在限制條件所在的欄位上。
但是SYS_C0017756限制條件為CHECK,因此會自動建立索引在相同的欄位上。*/
SQL> SELECT index_name,uniqueness FROM dba_indexes
2 WHERE owner='FRANK' AND table_name='T4';
INDEX_NAME UNIQUENES
------------------------------------ ------------------
T4_A_PK NONUNIQUE --因為T4_A_PK為DEFERRABLE
T4_B_IDX UNIQUE --因為T4_B_UK為NOT DEFERRABLE
SQL> ALTER TABLE frank.t4 DISABLE CONSTRAINT t4_a_pk;
SQL> ALTER TABLE frank.t4 DISABLE CONSTRAINT t4_b_uk;
SQL> SELECT index_name,uniqueness FROM dba_indexes
2 WHERE owner='FRANK' AND table_name='T4';
INDEX_NAME UNIQUENES
------------------------------ ------------------
T4_A_PK NONUNIQUE --因為Oracle會自動刪除因限制條件而自動建立的UNIQUE INDEX
SQL> alter table frank.t4 enable primary key;
--因為一個表格只有一個PRIMARY KEY,所以等同ENABLE CONSTRAINT t4_a_pk
SQL> alter table frank.t4 enable constraint t4_b_uk;
SQL> SELECT index_name,uniqueness FROM dba_indexes
2 WHERE owner='FRANK' AND table_name='T4';
INDEX_NAME UNIQUENES
------------------------------------ -------------------
T4_A_PK NONUNIQUE
T4_B_UK UNIQUE --因為T4_B_UK又被啟用
49.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> ALTER TABLEfrank.t4 DISABLE PRIMARY KEY DROP INDEX; --刪除自動建立的索引
SQL> ALTER TABLE frank.t4 DISABLE CONSTRAINT t4_b_uk KEEP INDEX; --保留自動建立的索引
SQL> SELECT index_name,uniqueness FROM dba_indexes
2 WHERE owner='FRANK' AND table_name='T4';
INDEX_NAME UNIQUENES
------------------------------------ ------------------
T4_B_UK UNIQUE
SQL> DROP INDEX frank.t4_b_uk; --刪除t4_b_uk索引
SQL> ALTER TABLE frank.t4
2 ENABLE CONSTRAINT t4_b_uk USING INDEX
3 (CREATE INDEX t4_b_idx ON frank.t4(b));
--使用USING INDEX可以指定索引的名字與型態(UNIQUE/NONUNIQUE),甚至於位於哪個TABLESPACE
SQL> SELECT constraint_name,constraint_type,status,deferrable,index_name
2 FROM dba_constraints
3 WHERE owner='FRANK' AND table_name='T4';
CONSTRAINT_NAME C STATUS DEFERRABLE INDEX_NAME
------------------------------ -- -------------------- ---------------------------------- ------------------------------
SYS_C0017756 C ENABLED NOT DEFERRABLE
T4_A_PK P DISABLED DEFERRABLE
T4_B_UK U ENABLED NOT DEFERRABLE T4_B_IDX
SQL> SELECT index_name,uniqueness FROM dba_indexes
2 WHERE owner='FRANK' AND table_name='T4';
INDEX_NAME UNIQUENES
------------------------------------ ------------------
T4_B_IDX NONUNIQUE
/*因為CREATE INDEX指令是建立NONUNIQUE INDEX,因此即便T4_B_UK為NOT DEFERRABLE限制,
還是建立一個NONUNIQUE INDEX。若要建立UNIQUE INDEX則要使用CREATE UNIQUE INDEX語法。*/
8.5 表格的操作
當表格建立後,經過一段時間的使用,將會產生許多的情況。根據不同情況,資料庫管理者可以採用不同
的指令來解決或達到資料庫使用者的要求。以下僅對一些常見的情況進行說明。
8.5.1 欄位操作(Column Operation)
新增欄位(Add Columns)
當表格建立後,因為某種原因需要新增欄位時,可以利用 ALTER TABLE ADD 的指令,新增一個新欄
位。但是新增的欄位將被放在 Row Piece 的最後面,如果新增的欄位沒有設定預設值,則所有 ROW 的
該欄位值都將是 NULL(空值)。因此如果該欄位需要 NOT NULL 限制條件,則一定要設定預設值。
SQL> DESC t_col_tab
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B VARCHAR2(10)
C DATE
SQL> SELECT * FROM t_col_tab;
A B C
---------- ---------- --------------
50.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
1 A 15-MAY-99
2B 16-SEP-04
SQL> ALTER TABLE t_col_tab
2 ADD (d VARCHAR2(10));
SQL> DESC t_col_tab
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B VARCHAR2(10)
C DATE
D VARCHAR2(10)
SQL> SELECT * FROM t_col_tab;
A B C D
---------- ---------- -------------------- -----------
1 A 15-MAY-99 --每筆新增的D欄位值都是空值
2 B 16-SEP-04
SQL> ALTER TABLE t_col_tab
2 ADD(e NUMBER NOT NULL);
ALTER TABLE t_col_tab
*
ERROR at line 1:
ORA-01758: table must be empty to add mandatory (NOT NULL) column
/*雖然錯誤訊息為只有空表格可以加上NOT NULL欄位,其實是因為新增的欄位值為空值,違反NOT NULL限
制條件*/
SQL> ALTER TABLE t_col_tab
2 ADD(e NUMBER DEFAULT 10 NOT NULL);
/*表格不是空的,還是可以加上有NOT NULL限制條件的欄位,只要該欄位有設定預設值。*/
SQL> DESC t_col_tab
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B VARCHAR2(10)
C DATE
D VARCHAR2(10)
E NOT NULL NUMBER
SQL> SELECT * FROM t_col_tab;
A B C D E
---------- ---------- -------------------- ----------- ----------
1 A 15-MAY-99 10
2 B 16-SEP-04 10
修改欄位(Modify Columns)
當現有欄位的資料型態、資料長度或限制條件(NOT NULL)需要改變時,可以使用 ALTER TABLE
MODIFY 的語法修改現有欄位的定義。
SQL> DESC t_col_tab
51.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
Name Null? Type
---------------------------------------------------------------------------- ----------------------------------------
A NUMBER
B VARCHAR2(10)
C DATE
D VARCHAR2(10)
E NOT NULL NUMBER
SQL> SELECT * FROM t_col_tab;
A B C D E
---------- ---------- -------------------- ----------- ----------
1 A 15-MAY-99 10
2 B 16-SEP-04 10
SQL> ALTER TABLE t_col_tab MODIFY(b CHAR(10));
/*CHAR與VARCHAR2可以互換,變大沒問題,但是變小則需要考慮資料寬度的問題。*/
SQL> DESC t_col_tab
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B CHAR(10)
C DATE
D VARCHAR2(10)
E NOT NULL NUMBER
SQL> ALTER TABLE t_col_tab MODIFY(b VARCHAR2(5));
ALTER TABLE t_col_tab MODIFY(b VARCHAR2(5))
*
ERROR at line 1:
ORA-01441: cannot decrease column length because some value is too big
/*因為現在的B欄位為CHAR(10),所以資料內容已經使用了10 Bytes。但因為想要將B欄位長度改為5 Byte
s,所以出現錯誤。不過如果B欄位值都是空值,則可以成功的轉換。*/
SQL> ALTER TABLE t_col_tab MODIFY(a VARCHAR2(10));
ALTER TABLE t_col_tab MODIFY(a VARCHAR2(10))
*
ERROR at line 1:
ORA-01439: column to be modified must be empty to change datatype
/*除CHAR與VARCHAR2之間的轉換外,其餘的資料型態轉換,都要在欄位值為空值得情況下,才能進行。*/
SQL> ALTER TABLE t_col_tab MODIFY(d NUMBER);
SQL> DESC t_col_tab
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B CHAR(10)
C DATE
D NUMBER
E NOT NULL NUMBER
更名(Rename Table/Column)
當表格建立完成後,才發現欄位名字與表格名字需要修改。此時可以利用 ALTER TABLE RENAME 語
法更改表格名字與欄位名字。當更名完成後,欄位值、權限(Privilege)、限制條件、索引都依然還可以使
用,不過當函數(Function)、程序(Procedure)與套件(Package)的程式碼中,有用到被更改前的表格名稱
或欄位名稱,則狀態變成 INVALID,需要修改程式碼與重新編譯(Complie)成 VALID 後,才能正常使用。
52.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> DESC t_col_tab
NameNull? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B CHAR(10)
C DATE
D NUMBER
E NOT NULL NUMBER
SQL> SELECT * FROM t_col_tab;
A B C D E
---------- ---------- -------------------- ----------- ----------
1 A 15-MAY-99 10
2 B 16-SEP-04 10
SQL> SELECT object_id,object_type,data_object_id
2> FROM dba_objects WHERE object_name='T_COL_TAB';
OBJECT_ID OBJECT_TYPE DATA_OBJECT_ID
---------------- --------------------- -------------------------
88653 TABLE 88653
SQL> ALTER TABLE t_col_tab RENAME TO test_col_opers;
SQL> DESC test_col_opers
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A NUMBER
B CHAR(10)
C DATE
D NUMBER
E NOT NULL NUMBER
SQL> SELECT object_id,object_type,data_object_id
2> FROM dba_objects WHERE object_name='TEST_COL_OPERS';
OBJECT_ID OBJECT_TYPE DATA_OBJECT_ID
---------------- --------------------- -------------------------
88653 TABLE 88653
SQL> SELECT * FROM t_col_opers;
A B C D E
---------- ---------- -------------------- ----------- ----------
1 A 15-MAY-99 10
2 B 16-SEP-04 10
SQL> ALTER TABLE test_col_opers RENAME COLUMN a TO a_col;
SQL> DESC test_col_opers
Name Null? Type
----------------------------------------------------------- ----------------- ----------------------------------------
A_COL NUMBER
B CHAR(10)
C DATE
D NUMBER
E NOT NULL NUMBER
SQL> SELECT * FROM t_col_opers;
53.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
A_COL B CD E
---------- ---------- -------------------- ----------- ----------
1 A 15-MAY-99 10
2 B 16-SEP-04 10
刪除欄位(Drop Column)
資料庫管理者可以隨時視需要刪除一個或多個欄位。以下為刪除欄位的一些限制:
不能刪除該表格所剩下的最後一個欄位。
不能刪除欄位當該表格為 SYS 所擁有。
不能刪除索引組織表格(Index Organization Table)的 Primary Key 欄位。
不能刪除 Domain Index 所在的欄位。
當使用 ALTER TABLE DROP COLUMN 指令時,被刪除欄位的表格將會被鎖定。因此在整個刪除過程
中,僅允許查詢操作,任何 DML 或 DDL 都不允許發生。同時 DROP COLUMN 會將每個 Row Piece 的
該欄位刪除,需要花費相當的時間,所以建議將 DROP COLUMN 的操作放在系統維護時期或系統離峰期
間進行,以避免影響到正常的交易。
SQL> DESC frank.t_col_opers
Name Null? Type
---------------------------------------------------- ------------------------ ----------------------------------------
A NUMBER
B CHAR(10)
C DATE
D NUMBER
E NOT NULL NUMBER
SQL> ALTER TABLE frank.t_col_opers DROP COLUMN B; --一次僅能刪除一個欄位
--ALTER TABLE frank.t_col_opers DROP (A,B),則可以同時刪除A,B兩個欄位
SQL> DESC frank.t_col_opers
Name Null? Type
---------------------------------------------------- ------------------------ ----------------------------------------
A NUMBER
C DATE
D NUMBER
E NOT NULL NUMBER
SQL> SELECT * FROM frank.t_col_opers;
A C D E
---------- ---------------- ---------- ----------
1 15-MAY-99 10
2 16-SEP-04 10
然而有時後必須在系統尖峰期間進行刪除表格欄位的操作,但又怕影響到現有的交易進行。為因應這種情
況,可以使用 ALTER TABLE SET UNUSED COLUMN,先將欄位更名,讓一般交易無法存取原來的欄
位。等到系統離峰時,在使用 ALTER TABLE DROP UNUSED COLUMNS 真正地刪除欄位,這樣便可
達到兩全其美的效果。
SQL> DESC frank.t_col_opers
Name Null? Type
---------------------------------------------------- ------------------------ ----------------------------------------
A NUMBER
C DATE
54.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
D NUMBER
E NOTNULL NUMBER
SQL> SELECT * FROM frank.t_col_opers;
A C D E
---------- ---------------- ---------- ----------
1 15-MAY-99 10
2 16-SEP-04 10
SQL> ALTER TABLE frank.t_col_opers SET UNUSED COLUMN c; --一次設定一個欄位
--ALTER TABLE frank.t_col_opers SET UNUSED (c,d),則可以一次設定多個欄位
SQL> DESC frank.t_col_opers
Name Null? Type
---------------------------------------------------- ------------------------ ----------------------------------------
A NUMBER
D NUMBER
E NOT NULL NUMBER
SQL> SELECT * FROM frank.t_col_opers;
A D E
----------- ---------- ----------
1 10
2 10
--沒看見C欄位,不代表它已經被刪除。其實只是被更名
SQL> SELECT column_id,column_name FROM dba_tab_cols
2 WHERE owner='FRANK' AND table_name='T_COL_OPERS';
COLUMN_ID COLUMN_NAME
------------------ ------------------------------
1 A
SYS_C00002_09032515:14:11$ --已更名,Oracle會自動略過此欄位
2 D
3 E
/*可以由DBA_UNUSED_COL_TABS/ALL_UNUSED_COL_TABS/USER_UNUSED_COL_TABS查知,哪
些表格已經被設定UNUSED COLUMN,但尚未DROP COLUMN。*/
SQL> SELECT * FROM dba_unused_col_tabs;
OWNER TABLE_NAME COUNT
------------------------------ ------------------------------ ----------------
FRANK T_COL_OPERS 1
SQL> ALTER TABLE frank.t_col_opers DROP UNUSED COLUMNS; --注意COLUMNS有加上複數S
SQL> SELECT * FROM dba_unused_col_tabs;
no rows selected --欄位已經被刪除
SQL> SELECT column_id,column_name FROM dba_tab_cols
2 WHERE owner='FRANK' AND table_name='T_COL_OPERS';
COLUMN_ID COLUMN_NAME
---------- ------------------------------
1 A
2 D
55.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
3 E
加密欄位(Encryption Column)
雖然Oracle Database 11g 可以加密整個表格空間,但若不是每個表格的每個欄位的資料都需要加密保
護時,則可以使用 Oracle Database 10g 開始提供的新功能︰透通資料加密(Transparent Data
Encryption-TDE)即可。當資料區塊被寫到資料檔前,使用加密演算法將欄位值加密,所以儲存在資料檔
的欄位值為加密後的資料。當資料區塊由資料檔被讀到緩衝區快取前,將自動被解密,因此在緩衝區快取
的欄位值為原本的資料。資料庫管理者如果想要使用 TDE,必須先建立錢包(Wallet)。
然後使用欄位宣告的方式,宣告某些欄位使用 TDE 即可。雖然 Oracle 資料庫支援以下 4 種加密演算
法︰3DES168、AES128、AES192、AES256,但是一個表格只能選用一種加密演算法。
但因為加密演算法的結果是確定性的(Deterministic),也就是說如果輸入的內容相同,則加密後的輸出結
果也會一樣。因此為加強透通資料加密的安全性,當宣告某個欄位使用加密時,TDE 將欄位值加上 16
Bytes 的隨機字串,之後才對修改後的欄位值進行加密。如此即便輸入的資料相同,加密後的結果也不會
相同,這個機制稱為 SALT。所以如果資料庫管理者想要在被加密的欄位上建立索引,必須使用 NO
SALT 取消自動加上隨機字串的功能。
SQL> CREATE TABLE frank.tab_encrypt
2 (col1 NUMBER,
3 col2 VARCHAR2(10) ENCRYPT,
4 col3 VARCHAR2(10) ENCRYPT NO SALT); --col3使用加密保護欄位資料,但不使用隨機字串
/*因為沒有指定所使用的加密演算法,所以將使用預設的演算法-AES192。*/
SQL> SELECT column_name,encryption_alg,salt FROM dba_encrypted_columns
2 WHERE owner='FRANK' AND table_name='TAB_ENCRYPT';
COLUMN_NAME ENCRYPTION_ALG SAL
------------------------------ ----------------------------- ------
COL2 AES 192 bits key YES
COL3 AES 192 bits key NO
SQL> CREATE TABLE frank.tab_encrypt2
2 (col1 NUMBER,
3 col2 VARCHAR2(10) ENCRYPT,
4 col3 VARCHAR2(10) ENCRYPT USING '3DES168');
/*因為有指定使用3DES168當做此表格的加密演算法,所以此表格中所有加密的欄位都將使用3DES168。*/
--可以透過dba_encrypted_columns查詢表格中,哪些欄位已被加密以及加密所使用的演算法。
SQL> SELECT column_name,encryption_alg,salt FROM dba_encrypted_columns
2 WHERE owner='FRANK' AND table_name='TAB_ENCRYPT';
COLUMN_NAME ENCRYPTION_ALG SAL
------------------------------ --------------------------------------- ------
COL2 3 Key Triple DES 168 bits key YES
COL3 3 Key Triple DES 168 bits key YES
56.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> ALTER TABLEfrank.tab_encrypt
2 MODIFY (col2 VARCHAR2(10) DECRYPT); --將col2取消加密
SQL> ALTER TABLE frank.tab_encrypt
2 REKEY USING 'AES128'; --改變表格所使用的加密演算法
SQL> ALTER TABLE frank.tab_encrypt
2 MODIFY (col3 VARCHAR2(10) ENCRYPT SALT); --將col3改為加密並加上隨機字串
SQL> SELECT column_name,encryption_alg,salt FROM dba_encrypted_columns
2 WHERE owner='FRANK' AND table_name='TAB_ENCRYPT';
COLUMN_NAME ENCRYPTION_ALG SAL
------------------------------ ----------------------------- ------
COL3 AES 128 bits key YES
雖然表格欄位加密可以增強資料庫的安全保護,但是也會增加欄位所佔用的空間。為保證加密後的資料正
確性,每個被加密的欄位值有 20 Bytes 的訊息驗證碼(Message Authentication Code),用來檢驗資料
的完整性,Oracle 資料庫使用 SHA-1 演算法。此外為避免加密結果容易被反算得出原始欄位值,因此使
用 SALT 機制,先將欄位值先加上 16 Bytes 的隨機字串,才進行加密。
同時當欄位值被加密前,原始的欄位值在還沒有加上隨機字串前,先將欄位值變成以 16 Bytes 為一個單
位,將欄位值變成 16 Bytes,32 Bytes,48 Bytes,…的欄位值。例如:欄位長度為 10 Bytes,則會變成
16 Bytes。而欄位值為 20 Bytes,則擴增為 32 Bytes。
某個欄位如果沒有使用 NO SALT 設定,且欄位長度為 10 Bytes。當加密後,真正的欄位儲存空間為 52
Bytes(16 Bytes+16 Bytes(隨機字串)+20 Bytes(Message Authentication Code)。若欄位長度為 20
Bytes,加密後所佔的空間為 68 Bytes(32 Bytes+16 Bytes(隨機字串)+20 Bytes(MAC 訊息)。
SQL> CREATE TABLE frank.test_encrypt
2 (a NUMBER,
3 b CHAR(1),
4 c CHAR(1) ENCRYPT,
5 d CHAR(1) ENCRYPT NO SALT,
6 e CHAR(20) ENCRYPT,
7 f CHAR(20) ENCRYPT NO SALT);
SQL> INSERT INTO frank.test_encrypt VALUES(1,'B','C','D','E','F'); --輸入測試資料
SQL> select * from frank.test_encrypt;
A B C D E F
----------- -- -- -- -------------------- --------------------
1 B C D E F
SQL> COMMIT;
--欄位B的內容值為66,佔1個Byte。ASCII Code 66代表B
SQL> SELECT dump(b) FROM frank.test_encrypt;
DUMP(B)
--------------------------------------------------------------------------------
Typ=96 Len=1: 66
--欄位C的內容值為67,佔1個Byte。ASCII Code 67代表C
SQL> SELECT dump(c) FROM frank.test_encrypt;
57.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
DUMP(C)
--------------------------------------------------------------------------------
Typ=96 Len=1: 67
--找到資料列所在的資料區塊號碼與資料檔號碼
SQL>SELECT dbms_rowid.rowid_relative_fno(rowid) fileno,
2 dbms_rowid.rowid_block_number(rowid) blockno
3 FROM frank.test_encrypt;
FILENO BLOCKNO
------------- -------------------------------
4 5781
SQL> SHOW PARAMETER user_dump_dest
NAME TYPE VALUE
------------------------------------ ----------- -------------------------------------------------------
user_dump_dest string /u01/app/oracle/diag/rdbms/orcl/orcl/trace
--將資料檔號碼為4的資料檔的第5781個資料區塊內容傾倒到USER_DUMP_DEST
SQL> ALTER SYSTEM DUMP DATAFILE 4 BLOCK 5781;
block_row_dump:
tab 0, row 0, @0x1ebc
tl: 220 fb: --H-FL-- lb: 0x1 cc: 6
col 0: [ 2] c1 02 /*欄位A,儲存空間佔2 Bytes*/
col 1: [ 1] 42 /*欄位B,儲存空間佔1 Bytes。42為16進位,轉為10進位為66,轉為ASCII Code則為B*/
/*欄位C,儲存空間佔52 Bytes。欄位C的欄位定義為CHAR(1) ENCRYPT,因此原始資料將變成16 Bytes,
加上16 Bytes的隨機字串(Salt)以及20 Bytes的MAC,所以52=16+16+20*/
col 2: [52] 05 f9 d0 05 71 50 4b f3 f8 ca 42 79 e0 e0 9b 1e cf 28 eb b3 8a 53 fa fe ad
00 d0 3b ec 18 af 24 c8 fb 93 af 86 ec 70 3e f5 21 62 af 47 4c 88 cb 98 a6
bc 42
/*欄位D ,儲存空間佔36 Bytes。欄位定義CHAR(1) ENCRYPT NO SALT,所以32=16(原始資料轉換為16
Bytes)+20(MAC)。*/
col 3: [36]
18 b3 b1 f3 9b e2 9a 13 ae 19 88 e0 cc 67 60 f4 10 06 d3 b8 3a bf e0 e3 d5
bb 7f 79 74 f5 dc 82 a3 32 08 1a
/*欄位E,儲存空間佔68 Bytes。因為欄位定義為CHAR(20),所以原始資料轉換為32 Bytes,因此68=32+16
(Salt)+20(MAC)*/
col 4: [68]
a4 a5 ed 67 32 d6 50 72 73 dc 1a a2 10 eb e4 4f 8a 14 be 3f 29 a0 7d fb d9
10 7a 7d a8 25 a0 f5 2f 74 9f fd d6 a4 c4 9e 0f e2 da fb 03 c9 1c 28 a7 ae
bf a5 73 16 a4 6b ea 01 59 54 16 4a b3 21 95 23 d1 6b
col 5: [52] /*欄位E,52=32+20(MAC)*/
19 2b 38 52 d5 41 d1 22 2e f6 e7 18 f1 17 ce 36 bb da d3 9b cb c8 c6 bc fe
95 85 0f 16 59 30 4c c5 20 75 cc cb 61 84 b4 a2 1a 94 77 c1 68 a7 cf 15 c0
86 e6
關閉訊息驗證碼(MAC)可以減少加密欄位所佔的空間,以及可以提昇效能,最多可達 30%。不過雖然關
閉訊息驗證碼的指令使用在欄位層次,不過同一個表格的所有欄位訊息驗證碼設定都是相同的。所以只要
任一加密欄位決定關閉訊息驗證碼,這個設定將會影響整個表格。
8.5.2 刪除表格(Drop Table)
當一個表格不再被需要,資料庫管理者可以決定刪除這個表格。一但表格被刪除後,就不能回復。當然
Oracle Database10g 之後,可以使用 Flash Drop 的功能將意外刪除的表格救回,不過無法保證可以百
分之百的成功救回。因此最安全的方法是,先匯出(Export)此表格的資料,再刪除表格。如果事後反悔,
便可以使用之前產生的匯出檔,重新匯入(Import)表格即可。
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
當然資源回收筒也可以人工清除,只要執行 PURGE 指令即可。當資源回收筒經過清除後,原本存在其中
的表格、索引,就再也無法被復原。
SQL>PURGE RECYCLEBIN; --清除階段作業使用者所有在資源回收筒的物件
SQL> PURGE TABLESPACE users USER frank; --清除表格空間USERS中,屬於frank的物件
SQL> PURGE DBA_RECYCLEBIN; --清除資料庫中所有在資源回收筒的物件
SQL> show user
USER is "SYS“
SQL> DROP TABLE frank.t1;
SQL> SELECT owner,original_name FROM dba_recyclebin
2 > WHERE owner=’FRANK’ AND original_name=’T1’ AND type=’TABLE’;
OWNER ORIGINAL_NAME
------------- --------------------------------
FRANK T1
SQL> FLASHBACK TABLE frank.t1 TO BEFORE DROP;
61.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> ALTER SESSIONSET recyclebin=off ; --在此階段作業中關閉資源回收筒的功能
SQL> DROP TABLE frank.t1;
SQL> SELECT owner,original_name FROM dba_recyclebin
2 > WHERE owner=’FRANK’ AND original_name=’T1’ AND type=’TABLE’;
no rows selected --frank.t1並沒有被更名,而是直接被刪除
8.5.3 截斷表格(Truncate Table)
截斷表格與刪除表格不同的地方在於,截斷表格僅刪除所有的資料,並保留第一個擴充區塊,其餘的擴充
區塊全部歸還表格空間成為可用的擴充區塊(Free Extent),同時索引也會一同被截斷。所以截斷後的表
格,除刪除所有的資料與丟棄部份擴充區塊(Extent)外,其他的物件如索引(Index)、限制條件
(Constraint)、觸發器(Trigger)等依然存在。
截斷表格為 DDL 指令,所以不能倒回其操作。其實截斷並不是一筆一筆地將資料列刪除,而是利用高水
位標記(High Water Mark)的概念達到刪除所有資料列的效果。
所謂的高水位標記其實是區段標頭(Segment Header)的一個記錄,記錄著這個區段中最後一個被使用過
的資料區塊(Block)位置。位在高水位標記以下的資料區塊都是曾經被使用過,但目前其中是否有資料列
則無法得知,需要實際存取該資料區塊後,方才能得知是否有資料列存在。至於那些位在高水位標記以上
的資料區塊,則是未曾被使用過的資料區塊,當然其中也不可能有資料列存在的可能。
TRUNCATE TABLE 指令將高水位標記的記錄改為第一個擴充區塊的第一個資料區塊,表示這個表格沒
有任何已使用過的區塊,當然也不會有任何資料列存在,因此這表格的所有資料列便能快速地刪除。至於
除了第一個擴充區塊以外的擴充區塊是否要被釋出,則根據 TRUNCATE TABLE 的選項:{DROP
STORAGE|REUSE STORAGE}來決定。
DROP STORAGE:這是 TRUNCATE TABLE 的預設選項。這個選項會丟棄多餘的擴充區塊,只留下第
一個擴充區塊。
REUSE STORAGE:會保留多餘的擴充區塊。因為如果表格重複地被新增、截斷、新增、截斷、新增,
使用 DROP STORAGE 選項會造成發生許多次的配置(Allocate)、丟棄擴充區塊的操作。使用 REUSE
STORAGE 選項可以減少配置、丟棄擴充區塊所帶來的成本。
SQL> CREATE TABLE frank.t1 AS SELECT * FROM dba_tables; --建立一個測試用表格
SQL> SELECT COUNT(*) FROM frank.t1;
COUNT(*)
---------------
2240
SQL> SELECT extent_id,block_id,blocks,bytes FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T1';
EXTENT_ID BLOCK_ID BLOCKS BYTES
----------------- --------------- ------------- ----------
0 1137 8 65536 --此Extent由第1137個Block開始,連續8個Block。
1 1145 8 65536
2 1153 8 65536
3 1161 8 65536
4 1169 8 65536
5 1177 8 65536
6 1185 8 65536
7 1193 8 65536
8 1201 8 65536
9 1209 8 65536
/*由以上結果,可以得知此Segmet共有10個Extent組成。以及每個Extent的起始Block位置與由幾個連續Bloc
k組成 。*/
62.
Chapter 8 Table
Oracle Database 11g 資料庫管理入門
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T1');
PL/SQLprocedure successfully completed.
SQL> SELECT num_rows,blocks FROM dba_tables WHERE owner='FRANK' AND table_name='T1';
NUM_ROWS BLOCKS
-------------------- ------------
2440 76
/*第76個資料區塊為目前最後用到的區塊,也是所謂的High Water Mark位置。記錄High Water Mark位置的
資訊放在Segment Header,即Segment的第一個Extent中的第一個Block。*/
SQL> SELECT header_file,header_block FROM dba_segments
2 WHERE owner='FRANK' AND segment_name='T1';
HEADER_FILE HEADER_BLOCK
------------------- ------------------------
4 1137 --Segment Header Block的位置
---------------------------------------------------------------------------------------------------------------------
Truncate前的Segment Header Block內容
Extent Header:: spare1: 0 spare2: 0 #extents: 10 #blocks: 79
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x010004be ext#: 9 blk#: 5 ext size: 8
#blocks in seg. hdr's freelists: 1
#blocks below: 76
/* High Water Mark為第10個extent的第5個Block,根據DBA_EXTENTS的資訊,發現ext#0到ext#8每個Ex
tent都是8個Block組成。所以已經使用過的Block共有8*9+5=77,不過還要扣除Segment Header Block不
算,因此DBA_TABLES的BLOCKS值為76。
---------------------------------------------------------------------------------------------------------------------
SQL> TRUNCATE TABLE frank.t1;
/*預設為DROP STORAGE,只保留第一個EXTENT,其餘全數丟棄。但若使用TRUNCATE TABLE frank.t1
REUSE STORAGE,則不會丟棄已經擁的Extent。*/
SQL> SELECT COUNT(*) FROM frank.t1;
COUNT(*)
--------------
0
SQL> SELECT extent_id,block_id,blocks,bytes FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T1';
EXTENT_ID BLOCK_ID BLOCKS BYTES
----------------- --------------- ------------- ----------
0 1137 8 65536
/*此Segment目前只剩下一個Extent,即此Segment的第一個Extent。*/
SQL> SELECT header_file,header_block FROM dba_segments
2 WHERE owner='FRANK' AND segment_name='T1';
HEADER_FILE HEADER_BLOCK
------------------- -----------------------
4 1137 --Segment Header Block還是相同的Block
---------------------------------------------------------------------------------------------------------------------
Truncate後的Segment Header Block內容