Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
第八章 表格
前言
Relational Database 一般翻譯為關連式資料庫,但這個中文翻譯常造成誤解,認為此種資料庫的表格
(Table)與表格之間一定存有某種關連(Relationship),因此才被稱作關連式資料庫。其實並不是如此,這
裡所說的關連(Relation)是 E.F.Codd 所用來當作儲存資料的結構,由於此種資料庫是由許多關連所組成,
所以才稱做關連式資料庫。本章節的重點便是介紹表格(Table),是資料庫用來儲存資料的結構,就是關
連式資料庫所謂的關連。
8.1 表格相關事項
在前面的章節中曾經提到,Oracle Database 在邏輯結構中,是由表格空間(Tablespace)所組成,表格空
間是由區段(Segment)所構成,區段為多個擴充區塊(Extent)的集合,而每個擴充區塊則為連續的資料區
塊(Data Block)所組成的。表格只是眾多區段的其中一種,然而卻是最重要的一種,因為所有的資料都存
放在表格中,無論是資料辭典(Data Dictionary)還是一般使用者的資料。
當一個表格初始建立時,其相關的定義如表格名稱、欄位名字、欄位型態以及限制條件等資訊是存放在資
料辭典中,而此表格的資料則需要儲存在額外空間。所謂的額外空間就是資料區塊,然而依 Oracle 資料
庫的規定,儲存空間的配置是以擴充區段為單位。所以一個表格建立之初,至少要配置一個擴充區塊以供
該表格儲存資料使用,區段則是屬於同一個表格的擴充區塊集合。不過當表格持續地被新增(Insert)、修
改(Update)、刪除(Delete)資料後,之前已配置的擴充區塊可能已經不敷使用,所以 Oracle 資料庫會自動
配置一個新的擴充區塊給該表格,直到該表格所能擁有的最大擴充區塊個數為止。
8.1.1 區塊的結構
資料區塊的結構可以分成三大部分:快取層(Cache Layer)、交易層(Transaction Layer)與資料層(Data
Layer) 。根據不同的物件,其資料區塊的結構有著些許的不同,此處以表格區塊為範例。
	
快取層(Cache Layer)
快取層中存放著此區塊位置(Data Block Address)、區塊型態(Block Type:TABLE/INDEX、UNDO、
TEMPORARY)、區塊格式(Block Format:v7/v8),系統改變號碼(System Change Number-縮寫為
SCN)以及區塊尾端(Tail:存放此區塊中最後 4 個 Bytes 資料,用來與區塊註腳(Block Footer)的內容比
對,檢查區塊內容是否正確)。此區域固定佔 20 Bytes 的空間。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
交易層(Transaction Layer)
交易層分為固定的交易層(Fixed Transaction Layer)與變動的交易層(Variable Transaction Layer)。固
定的交易層顧名思義的表示此層的大小是固定,不因為任何因素而改變其大小。而變動的交易層的大小則
因為有關係的交易列表(Interested Transaction List-縮寫為 ITL)的個數,而有不同的大小。不過變動的
交易層一但佔用空間後,之後即便同時對此資料區塊異動的交易數量減少,也不會減少 ITL 的數量,也就
是變動的交易層的空間只有可能增加,而不會減少。
固定的交易層:此區域放著資料區塊的型態(Block Type)、最後一次資料區塊進行清理(Block CleanOut)
的時間、 有幾個 ITL 在變動的交易層中。此外如果是可用區塊列表(Freelists)管理的表格區塊,則還有可
用區塊列表鏈結(FreeList Link)與可用空間的鎖定(FreeSpace Lock)。如果是自動區段空間管理
(Automatic Segment Space Management-縮寫為 ASSM)管理的表格區塊,則存放的是層級 1 的位元組
區塊(L1 Bitmap Block)的位置、區塊位置的範圍(DBA Range)與操作碼(OP Code),以及區塊的版本資
訊(Block Incarnation)。此區域固定佔 48 Bytes 的空間。
變動的交易層:這個區域用來放 Interested Transaction List(ITL),有時被稱做交易項目(Transaction
Entry),每個 ITL 佔 24 Bytes 空間。當建立表格時,可以使用 INITRANS 指定初始的 ITL 個數,預設值
為 1。MAXTRANS 在 10g 之後自動設為 255,表示最多可有 255 個 ITL 存在。當每個交易
(Transaction)想要進行異動區塊內容,不論是新增、異動、刪除資料列時,首先必須找到可用的 ITL,將
交易相關的資訊填入 ITL 中,之後才可以進行後續的操作。不過已用過的 ITL 可以重複被使用,但已產
生的 ITL 個數將不會減少,所以 Oracle 伺服器會儘可能地重複使用那些已存在的 ITL。若找不到可用的
ITL,還是會新增一個 ITL,ITL 個數可多達 255。
一般所稱的資料區塊標頭(Block Header)是指快取層與交易層。
資料層(Data Layer)
表格目錄(Table Directory)
此結構的主要用途為當表格為叢集表格(Cluster Table:叢集表格由一個或多個表格組成),同一個叢集表
格的資料區塊中可能有著屬於不同表格的資料列(Row)。因此必須使用表格目錄的內容來找到屬於特定表
格的資料列。不過如果表格是非叢集型態,表格目錄中只有一個表格索引(Table Index)即可。但是叢集表
格則根據此叢集表格由幾個表格組成,決定需要幾個表格索引。而每個表格索引需要 4 Bytes 空間。
資料列目錄(Row Directory)
在這個表格中的每一筆資料列(Row)都有一個記錄在資料列目錄(Row Directory)中。因為存放在資料列
資料區域中的每個資料列之間,並沒有特別的間隔符號,所以每次想要讀取某個資料列時,需要提供該資
料列的資料列辨識碼(Row ID),其中的資料列號碼(Row Number)就是指定讀取資料列目錄的哪一筆記錄。
而該筆記錄的內容則記載著某筆資料列的資料列標頭的位置,利用資料列目錄的內容便可以由資料區塊找
出所要的資料列。
當一筆資料列被新增到此區塊時,須先找到可用的資料列目錄結構,如果沒有任何現有的結構可供使用,
則會增加一個資料列目錄結構,用來存放該筆資料列的資料列標頭在資料層(Data Layer)的位置,每個資
料列目錄結構需要 2 Bytes 的空間。然而若此區塊中的資料列被刪除後,則該筆資料列所使用的資料列目
錄結構則可以被重複被之後新增的資料列使用,不過資料列目錄所佔的空間不會縮小,即使有一大堆空的
資料列目錄結構存在。
所謂的區塊負擔(Block Overhead)是由區塊標頭(Block Header)、表格目錄(Table Directory)及資料列目
錄(Row Directory)組成。
可用空間(Free Space)
整個資料區塊空間扣除區塊負擔與資料列所佔用空間後,所剩餘的空間稱做可用空間(Free Space)。可用
空間可供新增資料列或因異動資料列(異動後的資料列長度較異動前來的大)使用。同時可用空間也可供區
塊負擔因 ITL 個數增加或資料列目錄結構增加而需要的空間。在這樣的兩面夾擊之下,可用空間將會逐漸
地減少。不過當有些資料列因為刪除或異動而導致資料列資料所佔空間減少時,可用空間將因此而增加。
然而需要注意的是,區塊負擔已經使用的空間,將不會因為任何原因而減少。
資料列資料(Row Data)
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
由眾多的資料列片段(Row Piece)組成,也就是我們一般認知的資料列。資料列片段本身也是由資料列標
頭(Row Header)與欄位資料(Column Data)組成。
資料列標頭(Row Header)為資料列旗標(Row Flag)、鎖定位元組(Lock Byte)、欄位數量(Number Of
Columns)、叢集索引(Cluster Index)、鏈結資訊(Chaining Information)所組成。
資料列旗標(Row Flag):用來儲存此筆資料列的狀態,是否為單一資料列片段組成或者為鏈結資料列片段
(Chaining Row Piece)的第一個片段還是最後一個片段,也可以顯示這筆資料列已經被刪除等訊息。資料
列旗標需要佔用 1 Byte 空間。
鎖定位元組(Lock Byte):記錄此筆資料列是否被某個交易鎖定,若該資料列正被某個交易鎖定,則鎖定
位元組中將會儲存某個區塊負擔的 ITL 位置,而該 ITL 中有鎖定該資料列的交易的交易辨識碼
(Transaction ID)。交易辨識碼由還原區段號碼(Undo Segment Number)、槽號碼(Slot Number)與順序
號碼(Sequence Number)組成。鎖定位元組需要 1 Byte 的空間。
欄位個數(Number Of Columns):用來記錄該資料列的欄位資料(Column Data)中有存在幾個存放欄位長
度(Column Length)的結構。欄位個數佔用 1 Byte 的空間。
叢集鍵值索引(Cluster Key Index):只有當此區塊為叢集表格的資料區塊時,才需要這個結構標示資料列
為哪個叢集表格的成員(Cluster Table Member)。叢集鍵值索引需要 1 Byte 的空間。
連結資訊(Chaining Information):當發生資料列遷移(Row Migration)或資料列鏈結(Row Chaining)時,
需要在資料列標頭中記錄另外的資料列片段的位置。鏈結資訊需要 6 Bytes 的空間,其中包含資料列片段
所在的區塊位置(Data Block Address)及該區塊標頭的資料列目錄位置(Row Number)。
如果此表格不是叢集表格,同時也沒有發生資料列遷移或資料列鏈結的情況,則資料列標頭等於資料列
負擔,只需要 3 Bytes 的空間(Row Flag/Lock Byte/# Of Cols)即可。
欄位資料(Column Data)為真正欄位值儲存的地方,但是每個欄位值需要使用一組欄位長度(Column
Length)及欄位值(Column Value)來儲存。因為欄位與欄位之間沒有任何間隔符號,所以使用欄位長度來
說明該欄位值所佔的空間為多少。假設某個欄位長度的值為 10,表示欄位長度後的 10 Bytes 所儲存的
內容,為該欄位的值,也就是說該欄位值佔 10 Bytes 的空間。當欄位值(Column Value)所佔的空間在
250 Bytes 以下時,則相對應的欄位長度只需要 1 Byte 即可。若當欄位值(Column Value)所佔的空間超
過 250 Bytes 以上時,則欄位長度需要 3 Bytes 的空間。欄位值(Column Value)為空值(Null)時,則該欄
位的欄位長度的內容值將為 0,表示不需要欄位值(Column Value)。但若該空值的欄位為於資料列片段的
最後面,則連欄位長度都不需要儲存,以節省空間。
SQL> CREATE TABLE frank.test (a NUMBER,b NUMBER,c NUMBER);
SQL> SELECT * FROM frank.test;
A B C
----------- ------------ ----------
a 1 /*B欄位值為NULL*/
b /*B,C欄位值皆為NULL*/
--以下為表格frank.test的block dump
tab 0, row 0, @0x1f97
tl: 9 fb: --H-FL-- lb: 0x1 cc: 3 /*fb為Flag Byte即Row Flag。cc為Number Of Columns */
col 0: [ 1] 61 /*[1]為Column Length,表示後面1 Byte為Column Value也就是61*/
col 1: *NULL* /*空值僅需要Column Length即可*/
col 2: [ 2] c1 02 /*[2]為Column Length,表示後面2 Byte為Column Value也就是c1 02*/
tab 0, row 1, @0x1f92
tl: 5 fb: --H-FL-- lb: 0x1 cc: 1 /*cc為1,表示此row piece中只存放1個欄位的值,因為B,C皆為空值*/
col 0: [ 1] 62
區塊尾部(Tail)
存放此資料區塊的最後 4 個 Bytes 資料,用來與儲存在快取層(Cache Layer)的區塊尾端資料比對,檢查
兩者是否一致。若兩者不一致,則會出現 ORA-01578 區塊毀損的錯誤訊息。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
8.1.2 資料列與區塊的關係
當一個表格被建立之後,至少會配置一個擴充區段。所以目前這個表格所有的列將會存放在擴充區塊中,
但因為擴充區塊為連續的資料區塊組成,所以列實際是存放在資料區塊中。表格是資料列(Row)的集合,
列是欄位(Column)所構成的,欄位是由欄位名字與資料型態組成。
資料列鏈結(Row Chaining)
由於 Oracle 資料庫進行 I/O 的最小單位是一個資料區塊,所以通常建議一筆資料列應該要小於該表格的
資料區塊大小,這樣在讀取一筆資料列時,只需要讀 1 個資料區塊即可,這樣可以減少 I/O 成本。但是
建立表格時,不能指定此表格所使用的資料區塊大小。因為資料區塊的大小是在建立表格空間時所設定,
所以在建立表格之前時,需要慎重地選擇所使用的表格空間。因為當新增(Insert)操作將資料列新增到表
格時,如果如果資料列的大小大於表格所使用的資料區塊大小,這時該資料列將被切割為數個資料列片段
(Row Piece),然後將資料列片段分別存放在不同的資料區塊。而這些資料列片段透過資料列表頭(Row
Header)中的鏈結資料(Chaining Information)鏈結在一起,每個資料列片段的鏈結資料記錄著下一段的資
料列片段位置。所以當 Oracle 資料庫存取該資料行時,需要讀取多個資料區塊,才能將這筆資料列讀到
緩衝區快取(Buffer Cache)。因此整體的效能便受到拖累(因為 I/O 的數量增加)。
	
這種情況稱作資料列鏈接(Row Chaining),通常發生在新增操作居多,有時一些異動操作也會造成。發
生的原因可能是資料列太大或資料區塊太小,資料庫管理者可以透過正規化將資料列變小或將表格移動到
較大區塊的表格空間來解決資料列鏈結的問題。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
	
	
資料列遷移(Row Migration)
當一個資料區塊中已經存在的資料列,因為更新操作(Update)造成原本資料列變大。如果現在資料列所處
區塊的可用空間(Free Space)夠大,這筆資料列會依然位於現在的資料區塊中。但是若可用空間不夠大,
則 Oracle 資料庫會尋找另一個可用空間夠大的區塊(同一個區段),將整筆資料列都遷移到該區塊。但是因
為索引(Index)中已經將原來資料列的位置當成索引項(Index Entry)的一部份,所以資料庫在原本的資料
區塊中,將繼續保存原來的資料列標頭(Row Header),並將其中的鏈結資料(Chaining Info)填入新資料
列的位置,因此當 Oracle 資料庫透過索引存取該筆資料列時,會先讀取到建立索引當時,該資料列所在
的區塊,但是現在該資料列已經遷移到另一個資料區塊中,Oracle 資料庫只好再利用舊列表頭的鏈結資
料存取現在資料列的區塊,因此至少需要讀取兩個表格的資料區塊才能夠取得一筆資料列。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
	
這種情況稱做資料列遷移,並可以透過保留較大的可用空間解決,即增加表格的 PCTFREE 參數值。
PCTFREE 參數預設為 10,表示當一個資料區塊的可用空間佔資料區塊的比率(free space/block size)低
於 10%時,該資料區塊將被設定為滿區塊(Full Block),表示該資料區塊不能再被新增資料列,但是可以
被更新或刪除資料列。而所保留的可用空間便是提供空間給未來更新資料列時,可以用來容納更新後的資
料列。
若已經發生資料列遷移,則可以使用表格重組(Table Reorganization)來消弭資料列遷移的情況。因為表
格重組的操作,都是使用新增(Insert)的方法,而資料列遷移則是由更新(Update)所產生。所以經過表格
重組後,便可以消弭資料列遷移的問題。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
	
8.1.3 正規化(Normalization)
在資料庫設計的相關技巧中,正規化是管理人員耳熟能詳的基本技巧,而基本的正規化至少要達到第三正
規化,才有正規化的效果。正規化可以帶來以下的一些好處:1.避免資料重複。2.節省儲存空間。當然某
些情況下,正規化可能導致效能上的問題。這時可以適時地使用反正規化來增加整體的效能,不過在進行
反正規化之前,當然還是要先正規化,因為不是每個表格都需要反正規化。
第一正規化 (First Normal Form:1NF)
定義:每個表格中都要有主鍵(Primary Key)用以辨識資料列,而且表格中不能有重複的欄位,同時每個
欄位只能儲存一筆值。
說明:這個表格中學號可以用來當做 Primary Key,因為每個學生都有一個不重複的學號。但是各科成績
這個欄位內,儲存國文、英文、數學的資料,便違反第一正規化的每個欄位只能有一個值的限制。因此必
須將各科成績的欄位執分別存放到不同的欄位,例如:國文、英文、數學。現在這個表格已經符合第一正
規化的要求。
	
第二正規化(Second Normal Form:2NF)
定義:首先必定要滿足第一正規化的要求,而且表格中非主鍵的欄位,還必須跟主鍵有完全相依性。
說明:國文、數學、英文、總分都與學號有著完全的相依性。所以此表格也滿足第二正規化的要求。
額外說明︰假設某表格由學號、科目、成績、教室所組成。因為同一位學生有多個科目的成績,所以主鍵
學號與科目組成。同時每個欄位只有單一值,所以這個表格符合第一正規化。但是非主鍵欄位:上課教室,
只與主鍵中的科目相關,不與學號相關。這種情況稱作部份相依。而成績則與主鍵完全相依,因為每個成
學號 各科成績(國文,英文,數學) 總分
100 90,85,95 270
101 85,80,100 265
學號 國文 英文 數學 總分
100 90 85 95 270
101 85 80 100 265
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
績都是以特定學號與特定科目為分別。因此為符合第二正規化的要求,需要將上課教室移出本表格,另外
建立一個表格由科目與上課教室組成,這樣才會符合第二正規化的要求。
科目 上課教室
國文 A
英文 B
數學 A
第三正規化(Third Normal Form:3NF)
定義:當滿足第二正規化後,非主鍵欄位之間不能存在相依性。
說明:因為總分為國文、英文、數學等欄位的加總,所以這些非主鍵欄位之間有相依性。所以必須將總分
欄位由表格中移除,另外建立一個表格由學號與總分組成
如此便可以滿足第三正規化的要求。
學號 總分
100 270
101 265
	
正規化所造成的問題
如果不建立總分表格:
由 AP 人員必須將計算公式(國文+數學+英文)寫到程式中,這種方法的好處是,表格依然維持正規的設計,
但是若計算公式一但有所變動,則需要修改程式內容,以及必須將新的程式部署到各個客戶端。
學號 科目 成績 上課教室
100 國文 90 A
100 英文 85 B
100 數學 95 A
101 國文 85 A
101 英文 80 B
101 數學 100 A
學號 科目 成績
100 國文 90
100 英文 85
100 數學 95
101 國文 85
101 英文 80
101 數學 100
學號 國文 英文 數學 總分
100 90 85 95 270
101 85 80 100 265
學號 國文 英文 數學
100 90 85 95
101 85 80 100
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
建立一個視觀圖(VIEW),將計算公式產生總分的虛擬欄位(CREATE VIEW student_view AS SELECT
國文,數學,英文,(國文+數學+英文) as 總分 FROM student),這樣便可以透過視觀圖得到總分的資料。
這種方式的缺點是必須多花一些維護成本在視觀圖的管理上。
解決方案:自 Oracle Database11g 後可以採用虛擬欄位,解決此種問題。若使用反正規化的技巧解決此
問題。
如果建立總分表格:
必須使用結合(Join)指令才能得到各科成績與總分的結果,可能導致大量的結合指令發生,進而影響效能。
解決方案:可以使用叢集(Cluster)表格將兩個表格叢集起來,讓操作結合指令時,可以減少資料區塊的
I/O 量。也可以使用反正規化的技巧解決此問題。
反正規化
資料庫管理者也可以在 Student 表格上,多加一個總分的欄位,其內容值為國文、數學、英文的總和。
並利用觸發器(Trigger)或前端應用程式在相關分數(國文、數學、英文)變動時,自動維護總分的內容,以
保持資料的一致性。此方式即所謂的反正規化,可以增加查詢的效能,但也造成儲存空間的浪費,同時增
加了資料不一致的風險。所以使用反正規化前,必須仔細設計,避免相關的問題發生。
8.2 基本的資料型態
雖然 Oracle 資料庫支援許多種資料型態,但是正如”80-20”法則所發現的現象,絕大多數的表格中,
常用到的資料型態不脫文字、數字、日期等基本型態。以下便針對這些資料型態進行說明。
8.2.1 文字型態
此型態的欄位用來儲存文字資料,但是建立此種欄位前,需要注意資料寬度與編碼的選擇。
寬度
文字資料型態還可以依儲存空間分成:固定寬度(CHAR/NCAHR)與變動寬度
(VARCHAR2/NVARCHAR2/LONG/CLOB)兩種。
所謂固定寬度是說雖然輸入的欄位值小於該欄位的限制長度,但是實際儲存資料時,會先自動向右補足空
白後,才將欄位值的內容儲存到資料區塊中。這種方式較浪費空間,但是存取效率較變動寬度來得好,同
時也可以減少資料列遷移的發生。CHAR(x Byte|Char)表示此欄位最大只能存放 x Bytes(預設)或 x
Characters 以資料庫字符集編碼的資料。Byte 表示 x 的單位是 Byte,Char 表示 x 的單位為 Character。
當一個 Character(符號)需要多個 Bytes 才能編碼時,CHAR(1 Byte)與 CHAR(1 Char)的實際儲存空間便
不相同,CHAR(1 Byte)只能容納 1 個 Byte 的資料,而 CHAR(1 Char)則可以容納 1 個 Character 的資
料,跟據 Character 由幾個 Bytes 所編碼決定,實際可以容納多少 Bytes(1/2/3/4)的資料。CHAR 的資
料欄位最大能夠存放 2000 Bytes 的資料。NCHAR 也是 2000 Bytes 的限制,不過 NCHAR(10)只有一
種選擇,即 10 CHAR,不能使用 10 Byte 的方式。
變動寬度則是當輸入的欄位值小於該欄位的限制長度,直接使用欄位值的內容儲存到資料區塊中,不會補
上空白,可以節省資料區塊的空間。VARCHAR2(x Byte|Char) 表示此欄位最大只能存放 x Bytes(預設)
或 x Characters 以資料庫字符集編碼的資料。Byte 表示 x 的單位是 Byte,Char 表示 x 的單位為
Character。當一個 Character(符號)需要 n 個 Bytes 才能編碼時,CHAR(1 Byte)與 CHAR(1 Char)的實
際儲存空間便不相同,CHAR(1 Byte)只能容納 1 個 Byte 的資料,而 CHAR(1 Char)則可以容納 1 個
Character 的資料,跟據 Character 由幾個 Bytes 所編碼決定,實際可以容納多少 Bytes(1/2/3/4)的資
料。 VARCHAR2 的資料欄位最大能夠存放 4000 Bytes 的資料。NVARCHAR2 也是 4000 Bytes,不
過 NVARCHAR2(10)只有一種選擇,即 10 CHAR,不能使用 10 Byte 的方式。而 LONG 可以儲存到
2G bytes 的資料。
編碼
文字型態的資料型態可依編碼方式分成:資料庫字符集(CHAR/VARCHAR2/CLOB/LONG)與國際字符
集(NCHAR/NVARCHAR2/NCLOB)兩種。資料庫中的文字資料都是透過字符集將符號轉換成數字後,才
存放到資料區塊中。透過不同的編碼集轉換,即便是相同的符號,也可能轉換成不同的數字。當然如果使
用錯誤的字符集,便可能無法正確地將資料區塊中的數字轉換為原本的符號。字符集的相關說明請參考
4.2.3。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
CHAR/VARCHAR2/LONG/CLOB 都用來儲存文字資料,所使用的編碼為資料庫字元集(Database
Character Set),在建立資料庫時所指定的。通常不能再更改,不過在特殊情況下,可以更改。
從 Oracle8i 開始,新增以下三種資料型態:NCHAR/NVARCHAR2/NCLOB,也可以用來存放文字資料,
不過所使用的編碼方法為本國字元集(National Character Set)。自 Oracle9i 後只能由 Unicode 中的
UTF8(變動寬度)或 AL16UTF16(固定寬度)則一為之。
8.2.2 數字型態
數字型態欄位用來儲存數字資料,不過根據不同的數字資料,可以分成下列幾種型態。
NUMBER
NUMBER(P,S)是最常見的數字型態,可存放的資料從 10^
-130
到 10^
126
(不包含此數值),需要 1 到 22
Bytes 不等的儲存空間。
P 是 Precision(精確度)的縮寫,表示最多位數的有效十進位位數(digit),最多不能超過 38 個有效位數。
最大有效數位(Most Significant Digit)是數值的最左邊的非零位數,最低有效數字(Least Significant
Digit)為數值的最右邊的位數。因此 123.45 這個數值中,1 是 MSD 而則是 LSD。
S 是 Scale 的縮寫,可使用範圍為-84 到 127。
Scale 為正數,表示從小數點到最低有效數字的位數。
Scale 為負數時,表示從最大有效數字到小數點的位數,但不包括最大有效數字。同時表示自小數點左邊
S 位數上四捨五入。
當 Scale 值大於 Precision 時,表示由小數點後的第 S 位數向左,不能有超過 P 個位數。同時自小數點後
的第 S 位開始四捨五入。
範例︰
原始資料 資料型態 儲存結果
123.45 NUMBER
其實是宣告為 float 浮點數,P 為 38
123.45
123.45 NUMBER(3)
等同於 NUMBER(3,0)
123(小數點後四捨五入)
總精確度為 3 位,無小數點後位
數。
123.45 NUMBER(3,2) 無法儲存(3 表示最大精確度為 3 個
digit,且小數點後的 digit 佔 2 個),
原始資料共有 5 個 digit。
123.45 NUMBER(5,2) 123.45
123.45 NUMBER(6,2) 123.45
123.45 NUMBER(5,-1) 120(在小數點左邊的 1 位,進行四
捨五入,因為 3 不足 5,則捨去)
0.02345 NUMBER(4,5) 0.02345(小數點後第 5 位向左不能
有超過 4 個數字)
0.023456 NUMBER(4,5) 0.02346(小數點後第 5 位向左不能
有超過 4 個數字,並從小數點後第 5
位開始四捨五入,所以 56 進位成 6)
0.12345 NUMBER(4,5) 無法儲存(小數點後第 5 位向左不能
有超過 4 個數字,但 12345 共 5 個
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)
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
--------------------------------------------------------------------------------------------------------------
Typ=2 Len=20: 211,13,35,57,79,91,13,35,57,79,91,13,35,57,79,91,13,35,57,79
SQL> SELECT DUMP(a) FROM frank.t2_int;
DUMP(A)
--------------------------------------------------------------------------------------------------------------
Typ=2 Len=20: 211,13,35,57,79,91,13,35,57,79,91,13,35,57,79,91,13,35,57,80
SQL> SELECT t1.a-t2.a “T1-T2” FROM frank.t1_int,frank.t2_int;
T1-T2
--------
-1
SQL> SELECT t2.a-t1.a “T2-T1” FROM frank.t1_int,frank.t2_int;
T2-T1
--------
1
SQL> CREATE TABLE frank.t3_int(a INTEGER(10));
CREATE TABLE frank.t3_int(a INTEGER(10))
*
ERROR at line 1:
ORA-00907: missing right parenthesis
/*INTEGER一定是NUMBER(38,0),不能設定精確度*/
SQL> CREATE TABLE frank.t3_int (a INTEGER);
SQL> INSERT INTO frank.t3_int VALUES(123.4);
SQL> INSERT INTO frank.t3_int VALUES(234.5);
SQL> SELECT * FROM frank.t3_int;
A
-----------
123
235 /*切記小數點後會四捨五入,而不是無條件捨去*/
FLOAT
FLOAT 如同 INTEGER 一般,也是 NUMBER 的次型態,目前 FLOAT 的定義遵守的是 IEEE754 的標
準 。FLOAT 只能設定精確度(Precision)可由 1 到 126,但不能設定 Scale,但可以存入有小數的數值。
而且精確度並不是使用十進位呈現而是二進位。所以當 FLOAT(10)表示的是二進位精確度,需要轉成十
進位整數精確度,公式為二進位精確度*0.30103,若有小數則無條件進位。若要從十進位精確度轉成二
進位精確度,公式為十進位精確度*3.32193。
假設將 123.45 存入 FLOAT(5)的欄位中,則儲存後的值為 120。
實際的過程如下:
計算真正的精確度:FLOAT(5)的十進位精確度為 5*0.30103=1.50515,進位到最近的整數為 2。
數值轉換:123.45=1.2345*10^2
,因為精確度不能超過 2 位數,同時進行四捨五入,所以數值變成
1.2*10^2=120。
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;
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 來存放這些文字型態的資料。
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)的物件
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	資料庫管理入門	
邏輯資料列 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 開始,可以宣告一種欄位型態:虛擬欄位,這種欄位並沒有真正的消耗儲存空
間,只有存在於表格定義中。當此欄位被查詢時,才會使用欄位定義的公式計算欄位值。欄位定義的公式
可以包括任何合法的運算以及函數。
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;
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);
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 的系統權限以及表格擁有者必須有足夠的表格空間配額。
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 --表格名稱會轉換成大寫後,才存入資料辭典。
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則表示該擴充區
塊為幾個連續的資料區塊組成。*/
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
STORAGE 子句
建立表格時,可以利用 STORAGE 子句設定表格的擴充區塊(Extent)參數。一個表格直接限制其大小,
但是可以透過擴充區塊的設定,間接限制其大小。若沒有設定 STORAGE 參數,則會依照此表格所在的
表格空間設定值設定相關的 STORAGE 參數。
SQL> CREATE TABLE frank.t2
2 (a NUMBER,b VARCHAR2(10),c DATE)
3 TABLESPACE users
4 STORAGE(INITIAL 1M NEXT 1M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 100);
/* INITIAL 1M設定第一個EXTENT的大小。
NEXT 1M指定第二個EXTENT的大小。
PCTINCREASE 0表示第三個EXTENT的大小為上一個EXTENT(即第二個)*(1+pctincrease/100),並以此
類推,第四個的大小為第三個的大小*(1+PCTINCREASE/100)。
MINEXTENTS表示建立此區段時,至少要有配置幾個擴充區塊。
MAXEXTENTS表示此區段,最多不能超過幾個擴充區塊。*/
從Oracle11g之後可以使用一個新的STORAGE參數:MAXSIZE,直接指定這個表格的大小,不需要辛苦的
計算EXTENT的大小與個數。
SQL> CREATE TABLE frank.t2
2 (a NUMBER,b VARCHAR2(10),c DATE)
3 TABLESPACE users
4 STORAGE(MAXSIZE 10M); /*這個表格的總空間不能超過10M*/
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
	
8.3.2 暫時表格(Temporary Table)
一般的表格建立後,都會有相對的區段(Segment)產生,而且這個區段必須指定所在的表格空間。然而
Oracle8i 後新增一種表格型態:暫時表格(Temporary Table),此種表格不需事先建立區段,而是當每個
階段作業(Session)第一次將資料新增(INSERT)到該暫時表格時,才會在該階段作業所使用的暫時表格空
間或暫時表格事先設定的暫時表格空間上,建立一個暫時區段。因此一個暫時表格可能會有多個區段同時
存在,但是每個階段作業僅能存取自己產生的暫時區段,所以暫時區段不會被其他階段作業所存取。因此
當暫時表格進行異動時,不需要使用鎖定(Lock)以避免同時異動,因為沒有其他階段作業可以異動其內容,
也不用擔心暫時表格的內容被其他階段作業看到。如此可以減少資料異動時的操作成本,也提供資料私密
性的功能。同時暫時表格上可以建立索引、限制與授與物件權限等,也可以正常的執行 DDL 指令(除
Truncate 外)。
資料保存時限
ON COMMIT DELETE ROWS:
ON COMMIT DELETE ROWS 是暫時表格的預設參數,表示暫時表格中的資料僅在交易(Transaction)過
程中有效,當交易結束(COMMIT)時,暫時表格的暫時區段將自動被截斷(TRUNCATE)。
ON COMMIT PRESERVE ROWS:
ON COMMIT PRESERVE ROWS 表示,暫時表格的內容,可以跨越交易而存在,不過當該階段作業結束
時,暫時表格的暫時區段將會隨著階段作業結束而被丟棄,暫時表格中的資料自然也隨之丟棄。
SQL> CREATE GLOBAL TEMPORARY TABLE frank.t3
2 (a NUMBER,b VARCHAR2(10));
/*由於沒有指定所在的表格空間,因此暫時表格的暫時區段將會產生在使用者FRANK的暫時表格空間中。
而資料保存時限為ON COMMIT DELETE ROWS*/
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> CREATE GLOBAL TEMPORARY TABLE frank.t4
2 (a NUMBER,b VARCHAR2(10))
3 TABLESPACE tempfrank;
/*此暫時表格的暫時區段將會產生在暫時表格空間TEMPFRANK中。
而資料保存時限為ON COMMIT DELETE ROWS*/
SQL> CREATE GLOBAL TEMPORARY TABLE frank.t5
2 (a NUMBER,b VARCHAR2(10))
3 ON COMMIT PRESERVE ROWS;
/*由於沒有指定所在的表格空間,因此暫時表格的暫時區段將會產生在使用者FRANK的暫時表格空間中。
而資料保存時限為ON COMMIT PRESERVE ROWS*/
SQL> SELECT table_name,tablespace_name,duration
2 FROM dba_tables
3 WHERE owner='FRANK' AND table_name IN ('T3','T4','T5');
TABLE_NAME TABLESPACE_NAME DURATION
------------------------------ ----------------------------------- ----------------------------
T5 SYS$SESSION
T4 TEMPFRANK SYS$TRANSACTION
T3 SYS$TRANSACTION
SQL> COMMIT; /*結束交易*/
SQL> SELECT * FROM frank.t3;
no rows selected /*因為暫時表格中尚未有任何資料*/
SQL> SELECT * FROM frank.t5;
no rows selected /*因為暫時表格中尚未有任何資料*/
SQL> INSERT INTO frank.t3 VALUES(1,'A');
SQL> INSERT INTO frank.t5 VALUES(1,'A');
SQL> SELECT * FROM frank.t3;
A B
---------- ----------
1 A
SQL> SELECT * FROM frank.t5;
A B
---------- ----------
1 A
SQL> COMMIT; /*結束交易*/
SQL> SELECT * FROM frank.t3;
no rows selected /*因為frank.t3的保存期限為交易期間,所以當交易結束後,暫時表格也會被截斷*/
SQL> SELECT * FROM frank.t5;
A B
---------- ----------
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
1 A /*因為frank.t5的保存期限為階段作業期間,所以當交易結束後,暫時表格不會被截斷*/
SQL> exit /*結束此階段作業,此時frank.t5的暫時區段將為隨著階段作業的結束,而被丟棄*/
[oracle@ELinux ~]$ sqlplus frank/oracle
SQL> SELECT * FROM frank.t5;
no rows selected /*在此階段作業中,frank.t5尚未被新增任何資料,所以連暫時區段都還沒有產生*/
8.3.3 外部表格(External Table)
外部表格用來存取資料庫外的文字檔(Text File)或 Oracle 專屬格式檔內容。因此建立外部表格時,不會
產生區段、擴充區塊、資料區塊等儲存結構,只有表格相關的定義存放在資料辭典中。當存取外部表格時,
才透過相關設定從文字檔或 Oracle 專屬格式檔案中取得資料。同時因為資料存放在資料庫之外,所以僅
供查詢(Select),不能對外部表格內容進行異動(Insert/Update/Delete)。
目錄物件(Directory)
自 Oracle Database 9i 開始,Oracle 資料庫若需要存取檔案系統時,必須使用目錄物件以相對路徑的方
式存取檔案,以增加資料庫安全性。所以使用外部表格前,需要先建立目錄物件,並授權資料庫的使用者
可以讀寫該目錄物件下的檔案。
[oracle@ELinux ~]$ pwd
/home/oracle
[oracle@ELinux ~]$ mkdir external_demo
[oracle@ELinux ~]$ sqlplus / as sysdba
SQL> CREATE DIRECTORY external_demo AS '/home/oracle/external_demo';
/*若在UNIX環境下,請注意大小寫差異,因為UNIX的目錄與檔案名稱大小寫視作不同目錄或檔案。
SQL> GRANT READ,WRITE ON DIRECTORY external_demo TO frank;
/*授予frank可以讀、寫目錄物件下的檔案。
文字檔
從 Oracle Database 9i 開始,資料庫管理者便可以利用外部表格將當文字檔當作資料來源。此時需要使
用 ORACLE_LOADER 這個存取界面來讀取文字檔,其實 ORACLE_LOADER 透過 SQL*LOADER 的
幫助,因此相關的存取設定都與 SQL*LOADER 類似。ACCESS PARAMETERS 為整個外部表格定義中
最重要的一部份,包含如何將文字檔的資料對應到外部表格。其中 RECORDS DELIMITED 用來設定如
何將文字檔資料轉成一筆 Record,Record 為 Field 所組成的結構。FIELDS TERMINATED BY 決定如
何將 Record 分割成 Field。一般常見的是 FIELDS TERMINATED BY `,’即使用逗號將 Record 分割
成多個 Field。或者是 FIELDS TERMINATED BY WHITESPACE 則是使用空白來分割 Field。
[oracle@ELinux ~]$ cat /home/oracle/external_demo/emp.txt --測試範例文字檔
100,Frank,1999-05-15,15000
101,Linda,2004-09-16,20000
102,Gigi,2005-02-23,7800
103,Wilson,2007-09-06,10000
/*100,Frank,1999-05-15,15000為一個Record,Frank則為一個Field,Field之間依逗號為間隔*/
[oracle@ELinux ~]$ sqlplus frank/oracle
SQL> CREATE TABLE emp_ext
2 (empid NUMBER(4),
3 ename VARCHAR2(10),
4 hire_date DATE,
5 salary NUMBER(6))
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
6 ORGANIZATION EXTERNAL --宣告這是個外部表格
7 (TYPE ORACLE_LOADER --資料來源是文字檔,所以使用ORACLE_LOADER
8 DEFAULT DIRECTORY external_demo --文字檔所在目錄的相對目錄物件
9 ACCESS PARAMETERS --如何切割文字檔中的資料,以及如何對應到表格
10 (RECORDS DELIMITED BY NEWLINE --每筆Record以NEWLINE符號分隔,Record可視為Row
11 FIELDS TERMINATED BY ',' --每個Field以逗號為間隔
12 (empid,ename,hire_date DATE MASK 'YYYY-MM-DD',salary) --DATE MASK設定日期格式
13 )
14 LOCATION('emp.txt') --文字檔的名字
15 )REJECT LIMIT UNLIMITED --發生幾次錯誤後,讀取動作將會中斷。UNLIMITED為無限制
16 ;
SQL> DESC emp_ext
Name Null? Type
----------------------------------------------------- ----------- ----------------------------------------
EMPID NUMBER(4)
ENAME VARCHAR2(10)
HIRE_DATE DATE
SALARY NUMBER(6)
SQL> SELECT * FROM emp_ext;
EMPID ENAME HIRE_DATE SALARY
---------- -------------------- ---------------- ------------
100 Frank 15-MAY-99 15000
101 Linda 16-SEP-04 20000
102 Gigi 23-FEB-05 7800
103 Wilson 06-SEP-07 10000
SQL> SELECT object_id,data_object_id,object_type FROM user_objects
2 WHERE object_name='EMP_EXT';
OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
---------------- ------------------------- -------------------
89052 TABLE --沒有data_object_id表示這個物件沒有相對的區段。
SQL> SELECT extent_id,blocks,bytes FROM user_extents WHERE segment_name='EMP_EXT';
no rows selected --真的沒有此區段存在
/*除使用符號分割Field外,也可以使用固定寬度的方式分割Field*/
[oracle@ELinux ~]$ cat /home/oracle/external_demo/emp1.txt
100 Frank 1999-05-15 15000
101 Linda 2004-09-16 20000
102 Gigi 2005-02-23 7800
103 Wilson 2007-09-06 10000
[oracle@ELinux ~]$ sqlplus frank/oracle
SQL> CREATE TABLE emp1_ext
2 (empid NUMBER(4),
3 ename VARCHAR2(10),
4 hire_date DATE,
5 salary NUMBER(6))
6 ORGANIZATION EXTERNAL
7 (TYPE ORACLE_LOADER
8 DEFAULT DIRECTORY external_
9 ACCESS PARAMETERS
10 (RECORDS DELIMITED BY
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
11 FIELDS(empid(1:4),ename(6:15),hire_date(17:26) DATE mask 'YYYY-MM-DD',salary(28:33))
12 )
13 LOCATION('emp1.txt')
14 )REJECT LIMIT UNLIMITED
15 ;
/*使用固定寬度方式切割Record為Field,並對應到Column*/
SQL> SELECT * FROM emp_ext;
EMPID ENAME HIRE_DATE SALARY
---------- -------------------- ---------------- ------------
100 Frank 15-MAY-99 15000
101 Linda 16-SEP-04 20000
102 Gigi 23-FEB-05 7800
103 Wilson 06-SEP-07 10000
SQL> SELECT table_name,type_name FROM user_external_tables; --查詢有哪些外部表格
TABLE_NAME TYPE_NAME
------------------------------ ------------------------------
EMP_EXT ORACLE_LOADER
EMP1_EXT ORACLE_LOADER
	
Oracle 專屬格式檔
自 Oracle Database 10g 後,Oracle 增加一種新的外部表格格式,使用 Oracle 專屬格式檔案,而不是文
字檔。這種檔案是二進位格式,不過其中資訊是以 XML 格式存放。這種外部表格建立方式有兩種:
UNLOAD(卸載)與 LOAD(載入)。
卸載:使用 CTAS(Create Table As Select)語法建立一個新的外部表格,為何稱為卸載?原因為這個外
部表格的資料來源為子查詢(SubQuery),而將資料存放到某個 Oracle 專屬格式檔案中,而不是資料庫中
的另一個表格。
SQL> DESC emp
Name Null? Type
----------------------------------------------------- ----------- ----------------------------------------
EMPID NUMBER(3)
ENAME VARCHAR2(20)
HIRE_DATE DATE
SALARY NUMBER(7)
SQL> SELECT * FROM emp;
EMPID ENAME HIRE_DATE SALARY
---------- -------------------- ---------------- ------------
100 Frank 15-MAY-99 15000
101 Linda 16-SEP-04 20000
102 Gigi 23-FEB-05 7800
103 Wilson 06-SEP-07 10000
SQL> SELECT object_id,data_object_id,object_type FROM user_objects WHERE object_name='EMP';
OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
---------------- ------------------------- -------------------
89054 89054 TABLE --data_object_id不是空值,表示這是一個標準表格
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> SELECT extent_id,blocks,bytes FROM user_extents WHERE segment_name='EMP';
EXTENT_ID BLOCKS BYTES
---------------- ------------ ----------
0 8 65536 --此表格目前有一個擴充區塊
SQL> CREATE TABLE emp2_ext
2 ORGANIZATION EXTERNAL --宣告為外部表格
3 (TYPE ORACLE_DATAPUMP --檔案格式為Oracle專屬格式
4 DEFAULT DIRECTORY external_demo --使用external_demo這個目錄物件
5 LOCATION('emp2.dmp') --檔案的名字
6 )
7 AS
8 SELECT * FROM emp; --卸載的資料來源
SQL> DESC emp2_ext
Name Null? Type
----------------------------------------------------- ----------- ----------------------------------------
EMPID NUMBER(3)
ENAME VARCHAR2(20)
HIRE_DATE DATE
SALARY NUMBER(7)
SQL> SELECT * FROM emp2_ext;
EMPID ENAME HIRE_DATE SALARY
---------- -------------------- ---------------- ------------
100 Frank 15-MAY-99 15000
101 Linda 16-SEP-04 20000
102 Gigi 23-FEB-05 7800
103 Wilson 06-SEP-07 10000
SQL> SELECT object_id,data_object_id,object_type FROM user_objects
2 WHERE object_name='EMP2_EXT';
OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
---------------- ------------------------- -------------------
89055 TABLE --沒有data_object_id
SQL> SELECT default_directory_name,type_name FROM user_external_tables
2 WHERE table_name='EMP2_EXT';
DEFAULT_DIRECTORY_NAME TYPE_NAME
----------------------------------------- ------------------------------
EXTERNAL_DEMO ORACLE_DATAPUMP
--證實emp2_ext為外部表格,並且使用ORACLE_DATAPUMP界面存取檔案
SQL> SELECT directory_name,location FROM user_external_locations
2 WHERE table_name='EMP2_EXT'; --卸載後產生的檔案位置與名字
DIRECTORY_NAME LOCATION
---------------------------- -----------------
EXTERNAL_DEMO emp2.dmp
SQL> SELECT directory_path FROM dba_directories WHERE directory_name='EXTERNAL_DEMO';
DIRECTORY_PATH
--------------------------------------------------------------------------------------------------------------
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
/home/oracle/external_demo --檔案系統上真實目錄
SQL> !ls -l /home/oracle/external_demo/emp2.dmp
-rw-r----- 1 oracle oinstall 12288 Mar 27 17:00 /home/oracle/external_demo/emp2.dmp
--建立外部表格所產生出的Oracle專屬格式檔案,其中存放著emp2_ext表格的內容
載入:載入為建立一個外部表格,能夠對應到之前產生 Oracle 專屬格式檔案的內容,讓資料庫的使用者
不須將檔案內容載入資料庫,即可透過外部表格查詢其內容。
[oracle@ELinux ~]$ pwd
/home/oracle
[oracle@ELinux ~]$ mkdir ext_dir
[oracle@ELinux ~]$ cp /home/oracle/external_demo/emp2.dmp /home/oracle/ext_dir/emp3.dmp
[oracle@ELinux ~]$ ls -l /home/oracle/ext_dir/emp3.dmp
-rw-r----- 1 oracle oinstall 12288 3月 27 17:25 /home/oracle/ext_dir/emp3.dmp
[oracle@ELinux ~]$ sqlplus / as sysdba --因為要建立新的目錄物件
SQL> CREATE DIRECTORY ext_dir AS '/home/oracle/ext_dir';
SQL> GRANT READ,WRITE ON DIRECTORY ext_dir TO frank; --授權讓Frank可以讀寫ext_dir目錄物件
SQL> CONNECT frank/oracle --切換身份為frank
SQL> CREATE TABLE emp3_ext
2 (empid NUMBER(4),
3 ename VARCHAR2(10),
4 hire_date DATE,
5 salary NUMBER(6))
6 ORGANIZATION EXTERNAL
7 (TYPE ORACLE_DATAPUMP
8 DEFAULT DIRECTORY ext_dir
9 LOCATION('emp3.dmp')
10 )REJECT LIMIT UNLIMITED;
SQL> DESC emp3_ext
Name Null? Type
----------------------------------------------------- ----------- ----------------------------------------
EMPID NUMBER(3)
ENAME VARCHAR2(20)
HIRE_DATE DATE
SALARY NUMBER(7)
SQL> SELECT * FROM emp3_ext;
EMPID ENAME HIRE_DATE SALARY
------------- ---------------- ---------------- ------------
100 Frank 15-MAY-99 15000
101 Linda 16-SEP-04 20000
102 Gigi 23-FEB-05 7800
103 Wilson 06-SEP-07 10000
當外部表格建立以後,資料庫管理者可以透過 DBA_EXTERNAL_TABLES 查詢外部表格的屬性設定,
也可利用 DBA_EXTERNAL_LOCATIONS 的內容得知資料來源檔案的名字。如果想要知道表格一般資訊
則如同其他表格型態一樣,查詢 DBA_TABLES 即可。
由於外部表格的唯讀特性,因此 ALTER TABLE 的操作僅允許以下幾種:
變更 Reject limit:ALTER TABLE emp_ext REJECT LIMIT 10;
變更 Project 限制:ALTER TABLE emp_ext PROJECT COLUMN REFERENCED;
變更 Access Parameters:ALTER TABLE emp_ext ACCESS PARAMETERS(FIELDS
TERMINATED BY WHITESPACE);
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
變更 Location:ALTER TABLE emp_ext LOCATION(`emp.dat’);
8.4 限制條件(Constraint)
資料完整性(Data Integrity)是指表格中的資料遵守商業規則(Business Rule),意即表格中的資料經過一
些過濾,確定符合事先設定的規範。例如人事規章中規定,每位公司員工必須有一個不能重複的員工編號
以供辨識,同時員工必須歸屬某個已經存在的部門等。為達到此目標,可以使用下列的方法:
前端應用程式檢查:在將資料輸入資料表之前,先使用前端應用程式對資料進行檢查,確定符合資料符合
特定規則,才能存入資料表中。
後端資料庫觸發器檢查:在後端資料庫撰寫觸發器,當資料新增、異動、刪除時便會觸發資料檢查程序,
唯有符合規則的資料才能成功地新增、異動與刪除。
然而前兩種方法都是透過撰寫程式碼達到所需的功能,然而對資料庫管理人員來說,撰寫相關的程式可能
過於困難或麻煩。所以可以先使用資料庫內建的限制條件(Constraint)進行基本、簡單的檢查,當限制條
件做不到所要的功能時,才使用前端應用程式或後端資料庫的觸發器進行更複雜的處理與檢查。
使用限制條件有以下好處:
僅需要宣告即可,不須撰寫複雜的程式碼。
效率較程式碼來的好,因為由 Oracle 核心完成。
限制條件設定在資料庫端,所以任何前端應用程式都會受到規範,不會有遺漏。
8.4.1 限制條件的種類
Oracle 資料庫提供五種限制條件,讓使用者可以依需求選擇合適的限制。以下針對這五種限制條件進行
說明:
NOT NULL(不可為空值):NOT NULL 會限制欄位值不可以為空值。不過此種限制條件僅能針對單一欄
位。
UNIQUE KEY(唯一索引鍵):UNIQUE KEY 限制條件的欄位值必須是唯一,也就是欄位值不可與其他資
料列的欄位值重複,此種限制條件可以針對單一欄位或多個欄位所組成的複合值。
PRIMRAY KEY(主索引鍵):其實 PRIMARY KEY 是由 NOT NULL 與 UNIQUE KEY 所共同組成的,因
此欄位值不能為空值,也不能重覆。同時 PRIMARY KEY 限制可以針對一個欄位值或由多個欄位所組成
的複合值,不過一個表格僅能有一個 PRIMARY KEY 限制。若有其他欄位也需要如 PRIMARY KEY 限制
一般的功能,只能使用 NOT NULL 與 UNIQUE KEY 兩個限制結合來達到該要求。
FOREIGN KEY(外來索引鍵):FOREIGN KEY 這種限制條件是要求欄位值必須參考其他欄位中已有的內
容,不能隨心所欲地新增或異動欄位值,不過被參考的欄位必須已經有 PRIMARY KEY 或 UNIQUE KEY
限制條件存在。同時一般稱 FOREIGN KEY 所在的表格為子表格(CHILD TABLE),而被參考的表格為父
表格(PARENT TABLE)。
CHECK(檢查):此種限制是要求欄位值必須讓某個條件式為真(TRUE)後,才允許欄位新增或異動,此種
限制只能針對單一欄位。條件式的範例如下:CHECK(Salary>10000),此限制要求 Salary 欄位值必須
大於 10000。
宣告限制條件可以分成欄位層次(INLINE CONSTRAINT)與表格層次(OUT OF LINE CONSTRAINT)。
NOT NULL 只能使用欄位層次,但若為複合欄限制,則必須採用表格層次。同時採用欄位層次宣告限制
時,不需要在限制條件後加上欄位名稱,但表格層次則必須加上欄位名稱。此外限制條件的名字,可以藉
由 CONSTRAINT 參數設定,若沒有指定,則由 Oracle 伺服器自動產生-(SYS_Cnnnnn,nnnnn 為序列
號)。
SQL> CREATE TABLE frank.con_t1
2 (a NUMBER PRIMARY KEY, --欄位宣告後,立刻接著限制條件的宣告,此為欄位層次
3 b VARCHAR2(10),
4 c DATE,
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
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),
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
*
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
---------- -----------
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');
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
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');
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限制條件刪除*/
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
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';
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
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; --並未倒回整個交易
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
A B
---------- ----------
1 A
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
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
----------- --------------
1 10
2 2
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;
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> ALTER SESSION SET 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)存在。如果沒有任何索引存在,則會自動依限制條
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
件的可延緩(DEFERRABLE)設定,來建立唯一值索引(UNIQUE INDEX)或非唯一值索引(NON-UNIQUE
INDEX)。而且索引名稱預設與限制條件相同,除非使用 USING INDEX 的設定。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又被啟用
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> ALTER TABLE frank.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
---------- ---------- --------------
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
1 A 15-MAY-99
2 B 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
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 後,才能正常使用。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> DESC t_col_tab
Name Null? 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;
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
A_COL B C D 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
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
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
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
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
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> ALTER TABLE frank.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;
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	資料庫管理入門	
當表格被刪除時,此表格的定義與所有的擴充區塊將一併被刪除。當然這個表格上的索引、限制條件及觸
發器也會一起被刪除。因為刪除表格並沒有一筆一筆地將資料列刪除,而是將所使用的擴充區塊釋放,當
做表格空間的可用擴充區塊,所以不會有相對的重做項目與還原資料產生。
--刪除表格,但是可能可以利用Flashback Drop救回。
SQL>DROP TABLE frank.t_col_opers;
--若此表格有被其他表格使用外鍵限制參考,必須使用cascade constraints,同時將子表格的外鍵限制刪除。
SQL>DROP TABLE frank.t_col_opers CASCADE CONSTRAINTS;
--刪除表格,並且將表格由Recyclebin中清除,所以不能利用Flashback Drop方式救回此表格。
SQL>DROP TABLE frank.t_col_opers PURGE;
回溯表格刪除(Flashback Drop)
從 Oracle Database 10g 開始,表格的刪除不再是不可復原的操作。因為 Oracle Database 10g 使用資
源回收筒(Recyclebin)機制,讓資料庫管理者意外刪除重要表格時,可能有機會挽救這個錯誤的刪除。
當 RECYCELBIN 參數值為 ON(預設值),資料庫管理者刪除某個表格時,資料庫並不是立刻刪除該表格
的定義、刪除表格所使用的區段(也就是釋放所使用的擴充區塊),刪除表格上的索引、限制條件及觸發器。
而是將表格更名為以 BIN$前綴的名字,相關的索引與限制條件也是更名為 BIN$的名字,同時這個表格及
其上的索引所擁有的擴充區塊將被歸類為可用的擴充區塊(Free Extent)。這些 BIN$為前綴字的物件邏輯
上被劃分在資源回收筒中,但是實際上還是存在於原本的表格空間之中。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
雖然位於資源回收筒的擴充區塊已經被歸類為可用的擴充區塊,但是暫時還不會被配置給其他的區段使用。
除非表格空間中所有不屬於資源回收筒的可用的擴充區塊都已經用完,這時才會使用先進先出(First In
First Out)的方式,將資源回收筒中的可用擴充區塊配置給需要的區段。所以資源回收筒的存在並不會造
成空間的浪費,但是要注意資料安全的問題,因為被刪除的表格並不是立刻被刪除,除非使用 DROP
PURGE 或關閉資源回收筒功能。
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;
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> ALTER SESSION SET 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組成 。*/
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T1');
PL/SQL procedure 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內容
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 7
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x01000472 ext#: 0 blk#: 0 ext size: 7
#blocks in seg. hdr's freelists: 0
#blocks below: 0
/*High Water Mark的位置改為第1個Extent的第0個Block,表示此Segment沒有任何使用過的Block存在,當
然也不會有任何資料列存在。*/
---------------------------------------------------------------------------------------------------------------------
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T1');
SQL> SELECT num_rows,blocks FROM dba_tables WHERE owner='FRANK' AND table_name='T1';
NUM_ROWS BLOCKS
------------------ ---------------
0 0
8.5.4 表格重組(Table Reorganization)
表格重組是許多資料庫管理者喜歡進行的資料庫維護操作之一,另一個則是索引重組。可是許多資料庫管
理者並不知道表格重組的原因與用途,只是人云亦云地進行表格重組與索引重組。如果資料庫的可用性
(Availability)不會受到影響,那麼盲目地進行這些工作也不會有問題。但是若因為料庫必須保持 24x7 的
可用性,無法在離峰時期進行此類操作。這時表格重組或索引重組的效益就必須被嚴格檢視,而且必須大
於重建的成本,才有必要執行這些資料庫維護動作。
表格重組的用途
當表格經過多次 DML 操作後,每個的表格區塊都可能會有些資料列被刪除,而多出許多的可用空間。如
果表格往後還會有新的資料載入,則這些可用空間只是暫時閒置,往後新增的資料列還是可以重新使用這
些空間。也就是說空間浪費只是暫時的現象,這種情況便不需要進行表格重組。
如果因為表格都一直沒有新增的資料或新增的資料量遠小於刪除的資料量,也可能新增的資料使用附加
(Append)的方式等,造成許多的表格區塊有大量的可用空間。這時便可以說表格面臨表格片段(Table
Fragmentation)的問題,也就是空間浪費的問題。
但是這種情況對效能來說是否有不良的影響?首先如果存取這個表格的 SQL 敘述句絕大多數使用索引掃
描(Index Scan),這時便沒有效能上的問題。因為 Oracle 資料庫先使用索引,找到資料列的資料列辨識
碼,在使用資料列辨識碼讀取該筆資料列。但是因為 Oracle 資料庫的最小存取單位是一個資料區塊,所
以不管資料區塊中目前有幾筆資料列,還是要讀取整個區塊。雖然表格片段並不影響索引存取的效率,但
是表格空間的浪費可能還是需要注意。
但是如果全表格掃描存取表格,則對效能可能有重大的衝擊。因為全表格掃描是將表格所有用過的資料區
塊都讀到緩衝區快取後,才開始篩選符合條件的資料列。表格的總資料區塊數量越多,則讀取的資料區塊
個數也越多,當然效能也越差。這時就應該適時地進行表格重組的操作,也提昇 SQL 敘述句的效能。當
然建立索引也是一個可以選擇的解決方案。
表格移動(Table Move)
Oracle Database 8i 開始提供表格移動的功能,這個功能本來的用途是讓資料庫管理者可以將表格的區
段(Segment)搬移到另一個表格空間。但是因為搬移表格區段時,並不是真正的將區段搬移到另一個表格
空間。而是在該表格空間建立一個新的區段,將原本的區段當做資料來源,使用新增(Insert)操作將區段
的所有資料列都複製到新區段。然後修改資料辭典將新區段設定為該表格所使用的區段,最後刪除舊的區
段回收空間。
可是因為搬移區段所使用的方式為新增操作,所以新區段的資料區塊都會塞滿資料列,只留下 PCTFREE
所要求的空間比率給未來的更新使用。因此新區段的大小應該會比舊區段來得小,所以可以達到表格重組
的效果。此外表格搬移也會解決資料列遷移的問題,同時若搬移到較之前所使用的表格空間區塊大小更大
的表格空間,也可能一同解決資料鏈結的問題。
因為使用舊的區段當做資料來源,因此在表格搬移的期間,表格將被鎖定,而且鎖定的模式為獨佔模式
(Exclusive Mode)。所以在表格搬移的過程中,表格不能進行其他人的 DML 或 DDL 操作。而且表格搬
移的過程中,需要約當於舊區段大小的額外空間,因為舊的區段與新的區段同時存在。同時因為舊資料列
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
實際上已經被刪除,而新資料列則儲存在不同的區段。因此在表格搬移前所建立的索引,其中所記錄的資
料列 id 已經無法找到資料列,所以索引的狀態變為無法使用(Unusable)。因此必須重建索引,讓索引所
儲存的資料列 id 更新為新資料列的位置,這樣才能讓索引的狀態變為生效(Valid)。
--建立範例環境
SQL> CONNECT / AS SYSDBA
SQL> CREATE TABLE frank.t2 AS SELECT * FROM dba_tables;
SQL> CREATE INDEX frank.t2_name_idx ON frank.t2(table_name);
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2');
--目前表格所在的表格空間為USERS,以及共有2537筆資料列與使用87個資料區塊。
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T2';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ----------------- -----------
T2 USERS 2537 87
--目前索引的狀態為有效
SQL> SELECT index_name,status FROM dba_indexes
2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX';
INDEX_NAME STATUS
------------------------------ -----------
T2_NAME_IDX VALID
--表格搬移前,表格的物件編號為78698,以及區段編號為78698
SQL> SELECT object_name,object_id,data_object_id FROM dba_objects
2 WHERE owner='FRANK' AND object_name='T2' AND object_type='TABLE';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
------------------------------ ---------------- -------------------------
T2 78698 78698
--目前區段所使用的擴充區塊數量為11個,共有88個資料區塊。
SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T2';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------------- ----------- --------------- ------------
0 4 6753 8
1 4 6761 8
2 4 9921 8
3 4 9929 8
4 4 9937 8
5 4 9945 8
6 4 9953 8
7 4 9961 8
8 4 9969 8
9 4 9977 8
10 4 9985 8
11 rows selected.
--表格搬移前資料列的資料列id
SQL> SELECT rowid,owner,table_name FROM frank.t2
2 WHERE owner='HR' AND table_name='EMPLOYEES';
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
ROWID OWNER TABLE_NAME
--------------------------------- ------------------------------ ------------------------------
AAATNqAAEAAACcGAAd HR EMPLOYEES
--進行測試的資料刪除,造成資料區塊有部份空間浪費
SQL> DELETE frank.t2 WHERE owner IN ('SYS','SYSTEM');
SQL> COMMIT;
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2');
/*雖然資料列筆數減少為1513筆,但所使用的資料區塊數量與資料列筆數2537筆相同。
因此有部份資料區塊有空間浪費的現象。*/
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T2';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ----------------- ------------
T2 USERS 1513 87
--使用表格搬移將表格的區段搬移到example表格空間
SQL> ALTER TABLE frank.t2 MOVE TABLESPACE example;
/*若使用ALTER TABLE frank.t2 MOVE,則新的區段建立在舊區段所在的表格空間。因此除新區段所在的表
格空間不同外,其餘的動作則與MOVE TABLESPACE相同。*/
--表格搬移後,表格的物件編號不變依然為78698,但所使用的區段,已經變為區段編號為78702的區段
SQL> SELECT object_name,object_id,data_object_id FROM dba_objects
2 WHERE owner='FRANK' AND object_name='T2' AND object_type='TABLE';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
------------------------------ ---------------- -------------------------
T2 78698 78702
/*新區段擁有7個擴充區段,共56個資料區塊。新區段比舊區段小32個資料區塊,表示達到表格重組的效果。
同時擴充區塊的來源已經改為datafile# 5。*/
SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T2';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------------- ----------- ------------- ------------
0 5 465 8
1 5 473 8
2 5 481 8
3 5 489 8
4 5 497 8
5 5 505 8
6 5 513 8
7 rows selected.
--雖然資料列的內容相同,但是資料列的資料列id已經完全不同。因此索引需要重建。
SQL> SELECT rowid,owner,table_name FROM frank.t2
2 WHERE owner='HR' AND table_name='EMPLOYEES';
ROWID OWNER TABLE_NAME
-------------------------------- ------------------------------ ------------------------------
AAATNsAAFAAAAIFAAY HR EMPLOYEES
--索引狀態已經變成不可用(Unusable),因為索引項目的資料列id與表格重組後的資料列id不同。
SQL> SELECT index_name,status FROM dba_indexes
2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX';
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
INDEX_NAME STATUS
------------------------------ ---------------
T2_NAME_IDX UNUSABLE
--索引重建
SQL> ALTER INDEX frank.t2_name_idx REBUILD;
--索引重建後,索引的狀態變成有效(Valid)。
SQL> SELECT index_name,status FROM dba_indexes
2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX';
INDEX_NAME STATUS
------------------------------ -----------
T2_NAME_IDX VALID
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2');
--同樣的資料筆數(1513),重組後只需要使用54個資料區塊,而且表格的區段已經搬移到example表格空間。
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T2';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ------------------ -----------
T2 EXAMPLE 1513 54
縮小區段(Shrink Space)
Oracle Database 10g 提供一個新功能︰縮小區段,這個功能用來縮小區段所用到的空間,並且移動高水
位標記,回收位於新的高水位標記以上的區塊。
分成兩個階段,首先為 SHRINK SPACE COMPACT。這個操作需要對表格加上表格層次的共用鎖定
(TM:Row Exclusive),並對搬移資料列加上獨佔鎖定(Exclusive)。然後將表格的資料列盡可能地向區段
標頭集中,使用的方法就是使用一組新增、刪除資料列操作。先新增新的資料列,然後刪除舊的資料列但
新增的資料列儘可能靠近區段標頭。資料庫管理者可以線上重組表格,而不會阻擋其他人對此表格進行
DML 操作。同時縮小區段不會建立新的區段,所以可以減少表格重組過程中額外的空間需求。
Shrink space:取得表格獨佔鎖定,將高水位標記移動到目前資料區塊有資料列的最後一個資料區塊的位
置。之後釋放新高水位標記以上的資料區塊,因此表格空間將標示那些回收的擴充區塊為可用的擴充區塊。
--建立範例表格與索引
SQL> CREATE TABLE frank.t3 AS SELECT * FROM dba_objects WHERE object_id<=5000;
SQL> CREATE INDEX frank.t3_id_idx ON frank.t3(object_id);
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3');
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T3';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ----------------- ------------
T3 USERS 4928 71
SQL> SELECT index_name,status FROM dba_indexes
2 WHERE owner='FRANK' AND index_name='T3_ID_IDX';
INDEX_NAME STATUS
------------------------------- -----------
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
T3_ID_IDX VALID
SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T3';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------------- ----------- --------------- ------------
0 4 10889 8
1 4 10897 8
2 4 11273 8
3 4 11281 8
4 4 11289 8
5 4 11297 8
6 4 11545 8
7 4 11553 8
8 4 11561 8
9 rows selected.
--找出object_id為4999的資料列的資料列id
SQL> SELECT rowid,object_id,object_name FROM frank.t3 WHERE object_id=4999;
ROWID OBJECT_ID OBJECT_NAME
-------------------------------- ---------------- ------------------------------
AAATNyAAEAAAC0vABI 4999 DBA_SNAPSHOTS
--object_id為4999的資料列所在的資料區塊為11567,資料列為該區塊的第72筆資料列
SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_num,
2 dbms_rowid.rowid_row_number(rowid) row_num
3 FROM frank.t3 WHERE object_id=4999;
BLOCK_NUM ROW_NUM
------------------ ---------------
11567 72
--縮小區段之前的表格編號為78706,區段編號為78706
SQL> SELECT object_name,object_id,data_object_id FROM dba_objects
2 WHERE owner='FRANK' AND object_name='T3' AND object_type='TABLE';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
------------------------------ ---------------- -------------------------
T3 78706 78706
--進行縮小區段前,表格必須啟用資料列移動(Row Movement)的功能。
SQL> ALTER TABLE frank.t3 ENABLE ROW MOVEMENT;
--進行測試的資料刪除,造成資料區塊有部份空間浪費
SQL> DELETE frank.t3 WHERE MOD(object_id,2)=0;
SQL> COMMIT;
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3');
/*經過資料異動後,雖然資料筆數只剩下2460,但所使用的空間還是需要71個資料區塊。
可以考慮使用縮小區段將表格重組,並釋放多餘的資料區塊。*/
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T3';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ----------------- ------------
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
T3 USERS 2460 71
--只搬移資料列,但不重設高水位標記
SQL> ALTER TABLE frank.t3 SHRINK SPACE COMPACT;
--縮小區段並不會建立一個新的區段,所以縮小區段後,區段編號並沒有改變。
SQL> SELECT object_name,object_id,data_object_id FROM dba_objects
2 WHERE owner='FRANK' AND object_name='T3' AND object_type='TABLE';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
------------------------------ ---------------- -------------------------
T3 78706 78706
--進行縮小區段前,表格必須啟用資料列移動(Row Movement
--object_id為4999的資料列的資料列id已經與進行縮小區段之前的資料列id不同。
SQL> SELECT rowid,object_id,object_name FROM frank.t3 WHERE object_id=4999;
ROWID OBJECT_ID OBJECT_NAME
--------------------------------- ---------------- ------------------------------
AAATNyAAEAAACqMABI 4999 DBA_SNAPSHOTS
/*縮小區段之前object_id為4999的資料列位於編號11567的資料區塊的第72筆資料列
縮小區段之前object_id為4999的資料列位於編號10892的資料區塊的第72筆資料列*/
SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_num,
2 dbms_rowid.rowid_row_number(rowid) row_num
3 FROM frank.t3 WHERE object_id=4999;
BLOCK_NUM ROW_NUM
------------------ ---------------
10892 72
--因為縮小區段使用新增/刪除資料列的技巧來搬移資料列,因此索引的內容也一同被維護。
SQL> SELECT index_name,status FROM dba_indexes
2 WHERE owner='FRANK' AND index_name='T3_ID_IDX';
INDEX_NAME STATUS
------------------------------ ------------
T3_ID_IDX VALID
--縮小區段並沒有建立新的區段,所以擴充區塊並未改變。
SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T3';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------------- ----------- --------------- ------------
0 4 10889 8
1 4 10897 8
2 4 11273 8
3 4 11281 8
4 4 11289 8
5 4 11297 8
6 4 11545 8
7 4 11553 8
8 4 11561 8
9 rows selected.
--SHRINK SPACE將會搬移資料列,並會重新設定高水位標記,與釋放高於高水位標記以上的資料區塊。
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> ALTER TABLE frank.t3 SHRINK SPACE;
--收集表格的統計資料
SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3');
--經過SHRINK SPACE後,所使用的區塊個數減為33個,因此達到表格重組的效過。
SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables
2 WHERE owner='FRANK' AND table_name='T3';
TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS
------------------------------ ------------------------------ ----------------- ------------
T3 USERS 2460 33
--區段所擁有的擴充區塊個數也減少為5個,共40個資料區塊
SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents
2 WHERE owner='FRANK' AND segment_name='T3';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------------- ----------- --------------- ------------
0 4 10889 8
1 4 10897 8
2 4 11273 8
3 4 11281 8
4 4 11289 8
8.5.5 唯讀表格(Read-Only Table)
從 Oracle Database 11g 開始,可以將表格的狀態設定為唯讀,禁止對這個表格的異動。進行此項操作
之前,使用者必須擁有 ALTER TABLE 權限(針對自己擁有的表格)或 ALTER ANY TABLE 權限(針對任
何綱要下的表格)。
SQL> SELECT table_name,read_only FROM user_tables WHERE table_name='T1';
TABLE_NAME REA
------------------------------------- -------
T1 NO /*目前表格T1可以被異動*/
SQL> SELECT COUNT(*) FROM t1;
COUNT(*)
---------------
1
SQL> DELETE t1; /*成功刪除所有的ROWS*/
1 row deleted.
SQL> SELECT COUNT(*) FROM t1;
COUNT(*)
---------------
0
SQL> ROLLBACK; /*到回交易*/
SQL> ALTER TABLE t1 READ ONLY; /*將表格設定為唯讀*/
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
SQL> DELETE T1;
DELETE T1
*
ERROR at line 1:
ORA-12081: update operation not allowed on table "SYS"."T1"
SQL> SELECT table_name,read_only FROM user_tables WHERE table_name='T1';
TABLE_NAME REA
------------------------------------- -------
T1 YES
SQL> ALTER TABLE t1 READ WRITE ; /*將表格設定為可異動*/
8.5.6 表格回溯(Flashback Table)
表格回溯可以將一個表格或多個表格的內容,回溯到之前某個時間點的內容。而且與該表格相關的索引、
限制(Constraint)也將一同回溯。資料庫管理者也可以選擇啟動觸發器(Trigger),以維護資料的完整性
(Integrity)及一致性。
	
FLASHBACK TABLE table_name TO { { SCN | TIMESTAMP } expr [ { ENABLE | DISABLE } TRIGGER
S ] };
Chapter	8	Table	
Oracle	Database	11g	資料庫管理入門	
8.6 常用的資料辭典視觀表
	
結論
整個資料庫中,個數最多、佔用空間最大的區段就是表格,如果資料庫管理者對表格還不甚了解,那麼要
如何進行進一步的效能調整。然而 Oracle 資料庫所提供的表格型態不是只有本章所提及的標準表格、暫
時表格與外部表格而已,還有分割區表格(Partitioned Table)、叢集表格(Cluster Table)與索引組織表格
(Index Organized Table)等。不過這些表格型態通常用在特殊的環境下,在此就不多做說明。

Oracle 表格介紹

  • 1.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 第八章 表格 前言 Relational Database一般翻譯為關連式資料庫,但這個中文翻譯常造成誤解,認為此種資料庫的表格 (Table)與表格之間一定存有某種關連(Relationship),因此才被稱作關連式資料庫。其實並不是如此,這 裡所說的關連(Relation)是 E.F.Codd 所用來當作儲存資料的結構,由於此種資料庫是由許多關連所組成, 所以才稱做關連式資料庫。本章節的重點便是介紹表格(Table),是資料庫用來儲存資料的結構,就是關 連式資料庫所謂的關連。 8.1 表格相關事項 在前面的章節中曾經提到,Oracle Database 在邏輯結構中,是由表格空間(Tablespace)所組成,表格空 間是由區段(Segment)所構成,區段為多個擴充區塊(Extent)的集合,而每個擴充區塊則為連續的資料區 塊(Data Block)所組成的。表格只是眾多區段的其中一種,然而卻是最重要的一種,因為所有的資料都存 放在表格中,無論是資料辭典(Data Dictionary)還是一般使用者的資料。 當一個表格初始建立時,其相關的定義如表格名稱、欄位名字、欄位型態以及限制條件等資訊是存放在資 料辭典中,而此表格的資料則需要儲存在額外空間。所謂的額外空間就是資料區塊,然而依 Oracle 資料 庫的規定,儲存空間的配置是以擴充區段為單位。所以一個表格建立之初,至少要配置一個擴充區塊以供 該表格儲存資料使用,區段則是屬於同一個表格的擴充區塊集合。不過當表格持續地被新增(Insert)、修 改(Update)、刪除(Delete)資料後,之前已配置的擴充區塊可能已經不敷使用,所以 Oracle 資料庫會自動 配置一個新的擴充區塊給該表格,直到該表格所能擁有的最大擴充區塊個數為止。 8.1.1 區塊的結構 資料區塊的結構可以分成三大部分:快取層(Cache Layer)、交易層(Transaction Layer)與資料層(Data Layer) 。根據不同的物件,其資料區塊的結構有著些許的不同,此處以表格區塊為範例。 快取層(Cache Layer) 快取層中存放著此區塊位置(Data Block Address)、區塊型態(Block Type:TABLE/INDEX、UNDO、 TEMPORARY)、區塊格式(Block Format:v7/v8),系統改變號碼(System Change Number-縮寫為 SCN)以及區塊尾端(Tail:存放此區塊中最後 4 個 Bytes 資料,用來與區塊註腳(Block Footer)的內容比 對,檢查區塊內容是否正確)。此區域固定佔 20 Bytes 的空間。
  • 2.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 交易層(Transaction Layer) 交易層分為固定的交易層(Fixed TransactionLayer)與變動的交易層(Variable Transaction Layer)。固 定的交易層顧名思義的表示此層的大小是固定,不因為任何因素而改變其大小。而變動的交易層的大小則 因為有關係的交易列表(Interested Transaction List-縮寫為 ITL)的個數,而有不同的大小。不過變動的 交易層一但佔用空間後,之後即便同時對此資料區塊異動的交易數量減少,也不會減少 ITL 的數量,也就 是變動的交易層的空間只有可能增加,而不會減少。 固定的交易層:此區域放著資料區塊的型態(Block Type)、最後一次資料區塊進行清理(Block CleanOut) 的時間、 有幾個 ITL 在變動的交易層中。此外如果是可用區塊列表(Freelists)管理的表格區塊,則還有可 用區塊列表鏈結(FreeList Link)與可用空間的鎖定(FreeSpace Lock)。如果是自動區段空間管理 (Automatic Segment Space Management-縮寫為 ASSM)管理的表格區塊,則存放的是層級 1 的位元組 區塊(L1 Bitmap Block)的位置、區塊位置的範圍(DBA Range)與操作碼(OP Code),以及區塊的版本資 訊(Block Incarnation)。此區域固定佔 48 Bytes 的空間。 變動的交易層:這個區域用來放 Interested Transaction List(ITL),有時被稱做交易項目(Transaction Entry),每個 ITL 佔 24 Bytes 空間。當建立表格時,可以使用 INITRANS 指定初始的 ITL 個數,預設值 為 1。MAXTRANS 在 10g 之後自動設為 255,表示最多可有 255 個 ITL 存在。當每個交易 (Transaction)想要進行異動區塊內容,不論是新增、異動、刪除資料列時,首先必須找到可用的 ITL,將 交易相關的資訊填入 ITL 中,之後才可以進行後續的操作。不過已用過的 ITL 可以重複被使用,但已產 生的 ITL 個數將不會減少,所以 Oracle 伺服器會儘可能地重複使用那些已存在的 ITL。若找不到可用的 ITL,還是會新增一個 ITL,ITL 個數可多達 255。 一般所稱的資料區塊標頭(Block Header)是指快取層與交易層。 資料層(Data Layer) 表格目錄(Table Directory) 此結構的主要用途為當表格為叢集表格(Cluster Table:叢集表格由一個或多個表格組成),同一個叢集表 格的資料區塊中可能有著屬於不同表格的資料列(Row)。因此必須使用表格目錄的內容來找到屬於特定表 格的資料列。不過如果表格是非叢集型態,表格目錄中只有一個表格索引(Table Index)即可。但是叢集表 格則根據此叢集表格由幾個表格組成,決定需要幾個表格索引。而每個表格索引需要 4 Bytes 空間。 資料列目錄(Row Directory) 在這個表格中的每一筆資料列(Row)都有一個記錄在資料列目錄(Row Directory)中。因為存放在資料列 資料區域中的每個資料列之間,並沒有特別的間隔符號,所以每次想要讀取某個資料列時,需要提供該資 料列的資料列辨識碼(Row ID),其中的資料列號碼(Row Number)就是指定讀取資料列目錄的哪一筆記錄。 而該筆記錄的內容則記載著某筆資料列的資料列標頭的位置,利用資料列目錄的內容便可以由資料區塊找 出所要的資料列。 當一筆資料列被新增到此區塊時,須先找到可用的資料列目錄結構,如果沒有任何現有的結構可供使用, 則會增加一個資料列目錄結構,用來存放該筆資料列的資料列標頭在資料層(Data Layer)的位置,每個資 料列目錄結構需要 2 Bytes 的空間。然而若此區塊中的資料列被刪除後,則該筆資料列所使用的資料列目 錄結構則可以被重複被之後新增的資料列使用,不過資料列目錄所佔的空間不會縮小,即使有一大堆空的 資料列目錄結構存在。 所謂的區塊負擔(Block Overhead)是由區塊標頭(Block Header)、表格目錄(Table Directory)及資料列目 錄(Row Directory)組成。 可用空間(Free Space) 整個資料區塊空間扣除區塊負擔與資料列所佔用空間後,所剩餘的空間稱做可用空間(Free Space)。可用 空間可供新增資料列或因異動資料列(異動後的資料列長度較異動前來的大)使用。同時可用空間也可供區 塊負擔因 ITL 個數增加或資料列目錄結構增加而需要的空間。在這樣的兩面夾擊之下,可用空間將會逐漸 地減少。不過當有些資料列因為刪除或異動而導致資料列資料所佔空間減少時,可用空間將因此而增加。 然而需要注意的是,區塊負擔已經使用的空間,將不會因為任何原因而減少。 資料列資料(Row Data)
  • 3.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 由眾多的資料列片段(Row Piece)組成,也就是我們一般認知的資料列。資料列片段本身也是由資料列標 頭(Row Header)與欄位資料(ColumnData)組成。 資料列標頭(Row Header)為資料列旗標(Row Flag)、鎖定位元組(Lock Byte)、欄位數量(Number Of Columns)、叢集索引(Cluster Index)、鏈結資訊(Chaining Information)所組成。 資料列旗標(Row Flag):用來儲存此筆資料列的狀態,是否為單一資料列片段組成或者為鏈結資料列片段 (Chaining Row Piece)的第一個片段還是最後一個片段,也可以顯示這筆資料列已經被刪除等訊息。資料 列旗標需要佔用 1 Byte 空間。 鎖定位元組(Lock Byte):記錄此筆資料列是否被某個交易鎖定,若該資料列正被某個交易鎖定,則鎖定 位元組中將會儲存某個區塊負擔的 ITL 位置,而該 ITL 中有鎖定該資料列的交易的交易辨識碼 (Transaction ID)。交易辨識碼由還原區段號碼(Undo Segment Number)、槽號碼(Slot Number)與順序 號碼(Sequence Number)組成。鎖定位元組需要 1 Byte 的空間。 欄位個數(Number Of Columns):用來記錄該資料列的欄位資料(Column Data)中有存在幾個存放欄位長 度(Column Length)的結構。欄位個數佔用 1 Byte 的空間。 叢集鍵值索引(Cluster Key Index):只有當此區塊為叢集表格的資料區塊時,才需要這個結構標示資料列 為哪個叢集表格的成員(Cluster Table Member)。叢集鍵值索引需要 1 Byte 的空間。 連結資訊(Chaining Information):當發生資料列遷移(Row Migration)或資料列鏈結(Row Chaining)時, 需要在資料列標頭中記錄另外的資料列片段的位置。鏈結資訊需要 6 Bytes 的空間,其中包含資料列片段 所在的區塊位置(Data Block Address)及該區塊標頭的資料列目錄位置(Row Number)。 如果此表格不是叢集表格,同時也沒有發生資料列遷移或資料列鏈結的情況,則資料列標頭等於資料列 負擔,只需要 3 Bytes 的空間(Row Flag/Lock Byte/# Of Cols)即可。 欄位資料(Column Data)為真正欄位值儲存的地方,但是每個欄位值需要使用一組欄位長度(Column Length)及欄位值(Column Value)來儲存。因為欄位與欄位之間沒有任何間隔符號,所以使用欄位長度來 說明該欄位值所佔的空間為多少。假設某個欄位長度的值為 10,表示欄位長度後的 10 Bytes 所儲存的 內容,為該欄位的值,也就是說該欄位值佔 10 Bytes 的空間。當欄位值(Column Value)所佔的空間在 250 Bytes 以下時,則相對應的欄位長度只需要 1 Byte 即可。若當欄位值(Column Value)所佔的空間超 過 250 Bytes 以上時,則欄位長度需要 3 Bytes 的空間。欄位值(Column Value)為空值(Null)時,則該欄 位的欄位長度的內容值將為 0,表示不需要欄位值(Column Value)。但若該空值的欄位為於資料列片段的 最後面,則連欄位長度都不需要儲存,以節省空間。 SQL> CREATE TABLE frank.test (a NUMBER,b NUMBER,c NUMBER); SQL> SELECT * FROM frank.test; A B C ----------- ------------ ---------- a 1 /*B欄位值為NULL*/ b /*B,C欄位值皆為NULL*/ --以下為表格frank.test的block dump tab 0, row 0, @0x1f97 tl: 9 fb: --H-FL-- lb: 0x1 cc: 3 /*fb為Flag Byte即Row Flag。cc為Number Of Columns */ col 0: [ 1] 61 /*[1]為Column Length,表示後面1 Byte為Column Value也就是61*/ col 1: *NULL* /*空值僅需要Column Length即可*/ col 2: [ 2] c1 02 /*[2]為Column Length,表示後面2 Byte為Column Value也就是c1 02*/ tab 0, row 1, @0x1f92 tl: 5 fb: --H-FL-- lb: 0x1 cc: 1 /*cc為1,表示此row piece中只存放1個欄位的值,因為B,C皆為空值*/ col 0: [ 1] 62 區塊尾部(Tail) 存放此資料區塊的最後 4 個 Bytes 資料,用來與儲存在快取層(Cache Layer)的區塊尾端資料比對,檢查 兩者是否一致。若兩者不一致,則會出現 ORA-01578 區塊毀損的錯誤訊息。
  • 4.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 8.1.2 資料列與區塊的關係 當一個表格被建立之後,至少會配置一個擴充區段。所以目前這個表格所有的列將會存放在擴充區塊中, 但因為擴充區塊為連續的資料區塊組成,所以列實際是存放在資料區塊中。表格是資料列(Row)的集合, 列是欄位(Column)所構成的,欄位是由欄位名字與資料型態組成。 資料列鏈結(Row Chaining) 由於Oracle 資料庫進行 I/O 的最小單位是一個資料區塊,所以通常建議一筆資料列應該要小於該表格的 資料區塊大小,這樣在讀取一筆資料列時,只需要讀 1 個資料區塊即可,這樣可以減少 I/O 成本。但是 建立表格時,不能指定此表格所使用的資料區塊大小。因為資料區塊的大小是在建立表格空間時所設定, 所以在建立表格之前時,需要慎重地選擇所使用的表格空間。因為當新增(Insert)操作將資料列新增到表 格時,如果如果資料列的大小大於表格所使用的資料區塊大小,這時該資料列將被切割為數個資料列片段 (Row Piece),然後將資料列片段分別存放在不同的資料區塊。而這些資料列片段透過資料列表頭(Row Header)中的鏈結資料(Chaining Information)鏈結在一起,每個資料列片段的鏈結資料記錄著下一段的資 料列片段位置。所以當 Oracle 資料庫存取該資料行時,需要讀取多個資料區塊,才能將這筆資料列讀到 緩衝區快取(Buffer Cache)。因此整體的效能便受到拖累(因為 I/O 的數量增加)。 這種情況稱作資料列鏈接(Row Chaining),通常發生在新增操作居多,有時一些異動操作也會造成。發 生的原因可能是資料列太大或資料區塊太小,資料庫管理者可以透過正規化將資料列變小或將表格移動到 較大區塊的表格空間來解決資料列鏈結的問題。
  • 5.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 資料列遷移(Row Migration) 當一個資料區塊中已經存在的資料列,因為更新操作(Update)造成原本資料列變大。如果現在資料列所處 區塊的可用空間(Free Space)夠大,這筆資料列會依然位於現在的資料區塊中。但是若可用空間不夠大, 則Oracle 資料庫會尋找另一個可用空間夠大的區塊(同一個區段),將整筆資料列都遷移到該區塊。但是因 為索引(Index)中已經將原來資料列的位置當成索引項(Index Entry)的一部份,所以資料庫在原本的資料 區塊中,將繼續保存原來的資料列標頭(Row Header),並將其中的鏈結資料(Chaining Info)填入新資料 列的位置,因此當 Oracle 資料庫透過索引存取該筆資料列時,會先讀取到建立索引當時,該資料列所在 的區塊,但是現在該資料列已經遷移到另一個資料區塊中,Oracle 資料庫只好再利用舊列表頭的鏈結資 料存取現在資料列的區塊,因此至少需要讀取兩個表格的資料區塊才能夠取得一筆資料列。
  • 6.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 這種情況稱做資料列遷移,並可以透過保留較大的可用空間解決,即增加表格的 PCTFREE 參數值。 PCTFREE參數預設為 10,表示當一個資料區塊的可用空間佔資料區塊的比率(free space/block size)低 於 10%時,該資料區塊將被設定為滿區塊(Full Block),表示該資料區塊不能再被新增資料列,但是可以 被更新或刪除資料列。而所保留的可用空間便是提供空間給未來更新資料列時,可以用來容納更新後的資 料列。 若已經發生資料列遷移,則可以使用表格重組(Table Reorganization)來消弭資料列遷移的情況。因為表 格重組的操作,都是使用新增(Insert)的方法,而資料列遷移則是由更新(Update)所產生。所以經過表格 重組後,便可以消弭資料列遷移的問題。
  • 7.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 8.1.3 正規化(Normalization) 在資料庫設計的相關技巧中,正規化是管理人員耳熟能詳的基本技巧,而基本的正規化至少要達到第三正 規化,才有正規化的效果。正規化可以帶來以下的一些好處:1.避免資料重複。2.節省儲存空間。當然某 些情況下,正規化可能導致效能上的問題。這時可以適時地使用反正規化來增加整體的效能,不過在進行 反正規化之前,當然還是要先正規化,因為不是每個表格都需要反正規化。 第一正規化 (FirstNormal Form:1NF) 定義:每個表格中都要有主鍵(Primary Key)用以辨識資料列,而且表格中不能有重複的欄位,同時每個 欄位只能儲存一筆值。 說明:這個表格中學號可以用來當做 Primary Key,因為每個學生都有一個不重複的學號。但是各科成績 這個欄位內,儲存國文、英文、數學的資料,便違反第一正規化的每個欄位只能有一個值的限制。因此必 須將各科成績的欄位執分別存放到不同的欄位,例如:國文、英文、數學。現在這個表格已經符合第一正 規化的要求。 第二正規化(Second Normal Form:2NF) 定義:首先必定要滿足第一正規化的要求,而且表格中非主鍵的欄位,還必須跟主鍵有完全相依性。 說明:國文、數學、英文、總分都與學號有著完全的相依性。所以此表格也滿足第二正規化的要求。 額外說明︰假設某表格由學號、科目、成績、教室所組成。因為同一位學生有多個科目的成績,所以主鍵 學號與科目組成。同時每個欄位只有單一值,所以這個表格符合第一正規化。但是非主鍵欄位:上課教室, 只與主鍵中的科目相關,不與學號相關。這種情況稱作部份相依。而成績則與主鍵完全相依,因為每個成 學號 各科成績(國文,英文,數學) 總分 100 90,85,95 270 101 85,80,100 265 學號 國文 英文 數學 總分 100 90 85 95 270 101 85 80 100 265
  • 8.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 績都是以特定學號與特定科目為分別。因此為符合第二正規化的要求,需要將上課教室移出本表格,另外 建立一個表格由科目與上課教室組成,這樣才會符合第二正規化的要求。 科目 上課教室 國文 A 英文B 數學 A 第三正規化(Third Normal Form:3NF) 定義:當滿足第二正規化後,非主鍵欄位之間不能存在相依性。 說明:因為總分為國文、英文、數學等欄位的加總,所以這些非主鍵欄位之間有相依性。所以必須將總分 欄位由表格中移除,另外建立一個表格由學號與總分組成 如此便可以滿足第三正規化的要求。 學號 總分 100 270 101 265 正規化所造成的問題 如果不建立總分表格: 由 AP 人員必須將計算公式(國文+數學+英文)寫到程式中,這種方法的好處是,表格依然維持正規的設計, 但是若計算公式一但有所變動,則需要修改程式內容,以及必須將新的程式部署到各個客戶端。 學號 科目 成績 上課教室 100 國文 90 A 100 英文 85 B 100 數學 95 A 101 國文 85 A 101 英文 80 B 101 數學 100 A 學號 科目 成績 100 國文 90 100 英文 85 100 數學 95 101 國文 85 101 英文 80 101 數學 100 學號 國文 英文 數學 總分 100 90 85 95 270 101 85 80 100 265 學號 國文 英文 數學 100 90 85 95 101 85 80 100
  • 9.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 建立一個視觀圖(VIEW),將計算公式產生總分的虛擬欄位(CREATE VIEW student_viewAS SELECT 國文,數學,英文,(國文+數學+英文) as 總分 FROM student),這樣便可以透過視觀圖得到總分的資料。 這種方式的缺點是必須多花一些維護成本在視觀圖的管理上。 解決方案:自 Oracle Database11g 後可以採用虛擬欄位,解決此種問題。若使用反正規化的技巧解決此 問題。 如果建立總分表格: 必須使用結合(Join)指令才能得到各科成績與總分的結果,可能導致大量的結合指令發生,進而影響效能。 解決方案:可以使用叢集(Cluster)表格將兩個表格叢集起來,讓操作結合指令時,可以減少資料區塊的 I/O 量。也可以使用反正規化的技巧解決此問題。 反正規化 資料庫管理者也可以在 Student 表格上,多加一個總分的欄位,其內容值為國文、數學、英文的總和。 並利用觸發器(Trigger)或前端應用程式在相關分數(國文、數學、英文)變動時,自動維護總分的內容,以 保持資料的一致性。此方式即所謂的反正規化,可以增加查詢的效能,但也造成儲存空間的浪費,同時增 加了資料不一致的風險。所以使用反正規化前,必須仔細設計,避免相關的問題發生。 8.2 基本的資料型態 雖然 Oracle 資料庫支援許多種資料型態,但是正如”80-20”法則所發現的現象,絕大多數的表格中, 常用到的資料型態不脫文字、數字、日期等基本型態。以下便針對這些資料型態進行說明。 8.2.1 文字型態 此型態的欄位用來儲存文字資料,但是建立此種欄位前,需要注意資料寬度與編碼的選擇。 寬度 文字資料型態還可以依儲存空間分成:固定寬度(CHAR/NCAHR)與變動寬度 (VARCHAR2/NVARCHAR2/LONG/CLOB)兩種。 所謂固定寬度是說雖然輸入的欄位值小於該欄位的限制長度,但是實際儲存資料時,會先自動向右補足空 白後,才將欄位值的內容儲存到資料區塊中。這種方式較浪費空間,但是存取效率較變動寬度來得好,同 時也可以減少資料列遷移的發生。CHAR(x Byte|Char)表示此欄位最大只能存放 x Bytes(預設)或 x Characters 以資料庫字符集編碼的資料。Byte 表示 x 的單位是 Byte,Char 表示 x 的單位為 Character。 當一個 Character(符號)需要多個 Bytes 才能編碼時,CHAR(1 Byte)與 CHAR(1 Char)的實際儲存空間便 不相同,CHAR(1 Byte)只能容納 1 個 Byte 的資料,而 CHAR(1 Char)則可以容納 1 個 Character 的資 料,跟據 Character 由幾個 Bytes 所編碼決定,實際可以容納多少 Bytes(1/2/3/4)的資料。CHAR 的資 料欄位最大能夠存放 2000 Bytes 的資料。NCHAR 也是 2000 Bytes 的限制,不過 NCHAR(10)只有一 種選擇,即 10 CHAR,不能使用 10 Byte 的方式。 變動寬度則是當輸入的欄位值小於該欄位的限制長度,直接使用欄位值的內容儲存到資料區塊中,不會補 上空白,可以節省資料區塊的空間。VARCHAR2(x Byte|Char) 表示此欄位最大只能存放 x Bytes(預設) 或 x Characters 以資料庫字符集編碼的資料。Byte 表示 x 的單位是 Byte,Char 表示 x 的單位為 Character。當一個 Character(符號)需要 n 個 Bytes 才能編碼時,CHAR(1 Byte)與 CHAR(1 Char)的實 際儲存空間便不相同,CHAR(1 Byte)只能容納 1 個 Byte 的資料,而 CHAR(1 Char)則可以容納 1 個 Character 的資料,跟據 Character 由幾個 Bytes 所編碼決定,實際可以容納多少 Bytes(1/2/3/4)的資 料。 VARCHAR2 的資料欄位最大能夠存放 4000 Bytes 的資料。NVARCHAR2 也是 4000 Bytes,不 過 NVARCHAR2(10)只有一種選擇,即 10 CHAR,不能使用 10 Byte 的方式。而 LONG 可以儲存到 2G bytes 的資料。 編碼 文字型態的資料型態可依編碼方式分成:資料庫字符集(CHAR/VARCHAR2/CLOB/LONG)與國際字符 集(NCHAR/NVARCHAR2/NCLOB)兩種。資料庫中的文字資料都是透過字符集將符號轉換成數字後,才 存放到資料區塊中。透過不同的編碼集轉換,即便是相同的符號,也可能轉換成不同的數字。當然如果使 用錯誤的字符集,便可能無法正確地將資料區塊中的數字轉換為原本的符號。字符集的相關說明請參考 4.2.3。
  • 10.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 CHAR/VARCHAR2/LONG/CLOB 都用來儲存文字資料,所使用的編碼為資料庫字元集(Database Character Set),在建立資料庫時所指定的。通常不能再更改,不過在特殊情況下,可以更改。 從Oracle8i 開始,新增以下三種資料型態:NCHAR/NVARCHAR2/NCLOB,也可以用來存放文字資料, 不過所使用的編碼方法為本國字元集(National Character Set)。自 Oracle9i 後只能由 Unicode 中的 UTF8(變動寬度)或 AL16UTF16(固定寬度)則一為之。 8.2.2 數字型態 數字型態欄位用來儲存數字資料,不過根據不同的數字資料,可以分成下列幾種型態。 NUMBER NUMBER(P,S)是最常見的數字型態,可存放的資料從 10^ -130 到 10^ 126 (不包含此數值),需要 1 到 22 Bytes 不等的儲存空間。 P 是 Precision(精確度)的縮寫,表示最多位數的有效十進位位數(digit),最多不能超過 38 個有效位數。 最大有效數位(Most Significant Digit)是數值的最左邊的非零位數,最低有效數字(Least Significant Digit)為數值的最右邊的位數。因此 123.45 這個數值中,1 是 MSD 而則是 LSD。 S 是 Scale 的縮寫,可使用範圍為-84 到 127。 Scale 為正數,表示從小數點到最低有效數字的位數。 Scale 為負數時,表示從最大有效數字到小數點的位數,但不包括最大有效數字。同時表示自小數點左邊 S 位數上四捨五入。 當 Scale 值大於 Precision 時,表示由小數點後的第 S 位數向左,不能有超過 P 個位數。同時自小數點後 的第 S 位開始四捨五入。 範例︰ 原始資料 資料型態 儲存結果 123.45 NUMBER 其實是宣告為 float 浮點數,P 為 38 123.45 123.45 NUMBER(3) 等同於 NUMBER(3,0) 123(小數點後四捨五入) 總精確度為 3 位,無小數點後位 數。 123.45 NUMBER(3,2) 無法儲存(3 表示最大精確度為 3 個 digit,且小數點後的 digit 佔 2 個), 原始資料共有 5 個 digit。 123.45 NUMBER(5,2) 123.45 123.45 NUMBER(6,2) 123.45 123.45 NUMBER(5,-1) 120(在小數點左邊的 1 位,進行四 捨五入,因為 3 不足 5,則捨去) 0.02345 NUMBER(4,5) 0.02345(小數點後第 5 位向左不能 有超過 4 個數字) 0.023456 NUMBER(4,5) 0.02346(小數點後第 5 位向左不能 有超過 4 個數字,並從小數點後第 5 位開始四捨五入,所以 56 進位成 6) 0.12345 NUMBER(4,5) 無法儲存(小數點後第 5 位向左不能 有超過 4 個數字,但 12345 共 5 個
  • 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)
  • 12.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 -------------------------------------------------------------------------------------------------------------- Typ=2 Len=20: 211,13,35,57,79,91,13,35,57,79,91,13,35,57,79,91,13,35,57,79 SQL>SELECT DUMP(a) FROM frank.t2_int; DUMP(A) -------------------------------------------------------------------------------------------------------------- Typ=2 Len=20: 211,13,35,57,79,91,13,35,57,79,91,13,35,57,79,91,13,35,57,80 SQL> SELECT t1.a-t2.a “T1-T2” FROM frank.t1_int,frank.t2_int; T1-T2 -------- -1 SQL> SELECT t2.a-t1.a “T2-T1” FROM frank.t1_int,frank.t2_int; T2-T1 -------- 1 SQL> CREATE TABLE frank.t3_int(a INTEGER(10)); CREATE TABLE frank.t3_int(a INTEGER(10)) * ERROR at line 1: ORA-00907: missing right parenthesis /*INTEGER一定是NUMBER(38,0),不能設定精確度*/ SQL> CREATE TABLE frank.t3_int (a INTEGER); SQL> INSERT INTO frank.t3_int VALUES(123.4); SQL> INSERT INTO frank.t3_int VALUES(234.5); SQL> SELECT * FROM frank.t3_int; A ----------- 123 235 /*切記小數點後會四捨五入,而不是無條件捨去*/ FLOAT FLOAT 如同 INTEGER 一般,也是 NUMBER 的次型態,目前 FLOAT 的定義遵守的是 IEEE754 的標 準 。FLOAT 只能設定精確度(Precision)可由 1 到 126,但不能設定 Scale,但可以存入有小數的數值。 而且精確度並不是使用十進位呈現而是二進位。所以當 FLOAT(10)表示的是二進位精確度,需要轉成十 進位整數精確度,公式為二進位精確度*0.30103,若有小數則無條件進位。若要從十進位精確度轉成二 進位精確度,公式為十進位精確度*3.32193。 假設將 123.45 存入 FLOAT(5)的欄位中,則儲存後的值為 120。 實際的過程如下: 計算真正的精確度:FLOAT(5)的十進位精確度為 5*0.30103=1.50515,進位到最近的整數為 2。 數值轉換:123.45=1.2345*10^2 ,因為精確度不能超過 2 位數,同時進行四捨五入,所以數值變成 1.2*10^2=120。
  • 13.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 SQL> CREATE TABLEfrank.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 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則表示該擴充區 塊為幾個連續的資料區塊組成。*/
  • 23.
  • 24.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 STORAGE 子句 建立表格時,可以利用 STORAGE子句設定表格的擴充區塊(Extent)參數。一個表格直接限制其大小, 但是可以透過擴充區塊的設定,間接限制其大小。若沒有設定 STORAGE 參數,則會依照此表格所在的 表格空間設定值設定相關的 STORAGE 參數。 SQL> CREATE TABLE frank.t2 2 (a NUMBER,b VARCHAR2(10),c DATE) 3 TABLESPACE users 4 STORAGE(INITIAL 1M NEXT 1M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 100); /* INITIAL 1M設定第一個EXTENT的大小。 NEXT 1M指定第二個EXTENT的大小。 PCTINCREASE 0表示第三個EXTENT的大小為上一個EXTENT(即第二個)*(1+pctincrease/100),並以此 類推,第四個的大小為第三個的大小*(1+PCTINCREASE/100)。 MINEXTENTS表示建立此區段時,至少要有配置幾個擴充區塊。 MAXEXTENTS表示此區段,最多不能超過幾個擴充區塊。*/ 從Oracle11g之後可以使用一個新的STORAGE參數:MAXSIZE,直接指定這個表格的大小,不需要辛苦的 計算EXTENT的大小與個數。 SQL> CREATE TABLE frank.t2 2 (a NUMBER,b VARCHAR2(10),c DATE) 3 TABLESPACE users 4 STORAGE(MAXSIZE 10M); /*這個表格的總空間不能超過10M*/
  • 25.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 8.3.2 暫時表格(Temporary Table) 一般的表格建立後,都會有相對的區段(Segment)產生,而且這個區段必須指定所在的表格空間。然而 Oracle8i後新增一種表格型態:暫時表格(Temporary Table),此種表格不需事先建立區段,而是當每個 階段作業(Session)第一次將資料新增(INSERT)到該暫時表格時,才會在該階段作業所使用的暫時表格空 間或暫時表格事先設定的暫時表格空間上,建立一個暫時區段。因此一個暫時表格可能會有多個區段同時 存在,但是每個階段作業僅能存取自己產生的暫時區段,所以暫時區段不會被其他階段作業所存取。因此 當暫時表格進行異動時,不需要使用鎖定(Lock)以避免同時異動,因為沒有其他階段作業可以異動其內容, 也不用擔心暫時表格的內容被其他階段作業看到。如此可以減少資料異動時的操作成本,也提供資料私密 性的功能。同時暫時表格上可以建立索引、限制與授與物件權限等,也可以正常的執行 DDL 指令(除 Truncate 外)。 資料保存時限 ON COMMIT DELETE ROWS: ON COMMIT DELETE ROWS 是暫時表格的預設參數,表示暫時表格中的資料僅在交易(Transaction)過 程中有效,當交易結束(COMMIT)時,暫時表格的暫時區段將自動被截斷(TRUNCATE)。 ON COMMIT PRESERVE ROWS: ON COMMIT PRESERVE ROWS 表示,暫時表格的內容,可以跨越交易而存在,不過當該階段作業結束 時,暫時表格的暫時區段將會隨著階段作業結束而被丟棄,暫時表格中的資料自然也隨之丟棄。 SQL> CREATE GLOBAL TEMPORARY TABLE frank.t3 2 (a NUMBER,b VARCHAR2(10)); /*由於沒有指定所在的表格空間,因此暫時表格的暫時區段將會產生在使用者FRANK的暫時表格空間中。 而資料保存時限為ON COMMIT DELETE ROWS*/
  • 26.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 SQL> CREATE GLOBALTEMPORARY TABLE frank.t4 2 (a NUMBER,b VARCHAR2(10)) 3 TABLESPACE tempfrank; /*此暫時表格的暫時區段將會產生在暫時表格空間TEMPFRANK中。 而資料保存時限為ON COMMIT DELETE ROWS*/ SQL> CREATE GLOBAL TEMPORARY TABLE frank.t5 2 (a NUMBER,b VARCHAR2(10)) 3 ON COMMIT PRESERVE ROWS; /*由於沒有指定所在的表格空間,因此暫時表格的暫時區段將會產生在使用者FRANK的暫時表格空間中。 而資料保存時限為ON COMMIT PRESERVE ROWS*/ SQL> SELECT table_name,tablespace_name,duration 2 FROM dba_tables 3 WHERE owner='FRANK' AND table_name IN ('T3','T4','T5'); TABLE_NAME TABLESPACE_NAME DURATION ------------------------------ ----------------------------------- ---------------------------- T5 SYS$SESSION T4 TEMPFRANK SYS$TRANSACTION T3 SYS$TRANSACTION SQL> COMMIT; /*結束交易*/ SQL> SELECT * FROM frank.t3; no rows selected /*因為暫時表格中尚未有任何資料*/ SQL> SELECT * FROM frank.t5; no rows selected /*因為暫時表格中尚未有任何資料*/ SQL> INSERT INTO frank.t3 VALUES(1,'A'); SQL> INSERT INTO frank.t5 VALUES(1,'A'); SQL> SELECT * FROM frank.t3; A B ---------- ---------- 1 A SQL> SELECT * FROM frank.t5; A B ---------- ---------- 1 A SQL> COMMIT; /*結束交易*/ SQL> SELECT * FROM frank.t3; no rows selected /*因為frank.t3的保存期限為交易期間,所以當交易結束後,暫時表格也會被截斷*/ SQL> SELECT * FROM frank.t5; A B ---------- ----------
  • 27.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 1 A /*因為frank.t5的保存期限為階段作業期間,所以當交易結束後,暫時表格不會被截斷*/ SQL>exit /*結束此階段作業,此時frank.t5的暫時區段將為隨著階段作業的結束,而被丟棄*/ [oracle@ELinux ~]$ sqlplus frank/oracle SQL> SELECT * FROM frank.t5; no rows selected /*在此階段作業中,frank.t5尚未被新增任何資料,所以連暫時區段都還沒有產生*/ 8.3.3 外部表格(External Table) 外部表格用來存取資料庫外的文字檔(Text File)或 Oracle 專屬格式檔內容。因此建立外部表格時,不會 產生區段、擴充區塊、資料區塊等儲存結構,只有表格相關的定義存放在資料辭典中。當存取外部表格時, 才透過相關設定從文字檔或 Oracle 專屬格式檔案中取得資料。同時因為資料存放在資料庫之外,所以僅 供查詢(Select),不能對外部表格內容進行異動(Insert/Update/Delete)。 目錄物件(Directory) 自 Oracle Database 9i 開始,Oracle 資料庫若需要存取檔案系統時,必須使用目錄物件以相對路徑的方 式存取檔案,以增加資料庫安全性。所以使用外部表格前,需要先建立目錄物件,並授權資料庫的使用者 可以讀寫該目錄物件下的檔案。 [oracle@ELinux ~]$ pwd /home/oracle [oracle@ELinux ~]$ mkdir external_demo [oracle@ELinux ~]$ sqlplus / as sysdba SQL> CREATE DIRECTORY external_demo AS '/home/oracle/external_demo'; /*若在UNIX環境下,請注意大小寫差異,因為UNIX的目錄與檔案名稱大小寫視作不同目錄或檔案。 SQL> GRANT READ,WRITE ON DIRECTORY external_demo TO frank; /*授予frank可以讀、寫目錄物件下的檔案。 文字檔 從 Oracle Database 9i 開始,資料庫管理者便可以利用外部表格將當文字檔當作資料來源。此時需要使 用 ORACLE_LOADER 這個存取界面來讀取文字檔,其實 ORACLE_LOADER 透過 SQL*LOADER 的 幫助,因此相關的存取設定都與 SQL*LOADER 類似。ACCESS PARAMETERS 為整個外部表格定義中 最重要的一部份,包含如何將文字檔的資料對應到外部表格。其中 RECORDS DELIMITED 用來設定如 何將文字檔資料轉成一筆 Record,Record 為 Field 所組成的結構。FIELDS TERMINATED BY 決定如 何將 Record 分割成 Field。一般常見的是 FIELDS TERMINATED BY `,’即使用逗號將 Record 分割 成多個 Field。或者是 FIELDS TERMINATED BY WHITESPACE 則是使用空白來分割 Field。 [oracle@ELinux ~]$ cat /home/oracle/external_demo/emp.txt --測試範例文字檔 100,Frank,1999-05-15,15000 101,Linda,2004-09-16,20000 102,Gigi,2005-02-23,7800 103,Wilson,2007-09-06,10000 /*100,Frank,1999-05-15,15000為一個Record,Frank則為一個Field,Field之間依逗號為間隔*/ [oracle@ELinux ~]$ sqlplus frank/oracle SQL> CREATE TABLE emp_ext 2 (empid NUMBER(4), 3 ename VARCHAR2(10), 4 hire_date DATE, 5 salary NUMBER(6))
  • 28.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 6 ORGANIZATION EXTERNAL--宣告這是個外部表格 7 (TYPE ORACLE_LOADER --資料來源是文字檔,所以使用ORACLE_LOADER 8 DEFAULT DIRECTORY external_demo --文字檔所在目錄的相對目錄物件 9 ACCESS PARAMETERS --如何切割文字檔中的資料,以及如何對應到表格 10 (RECORDS DELIMITED BY NEWLINE --每筆Record以NEWLINE符號分隔,Record可視為Row 11 FIELDS TERMINATED BY ',' --每個Field以逗號為間隔 12 (empid,ename,hire_date DATE MASK 'YYYY-MM-DD',salary) --DATE MASK設定日期格式 13 ) 14 LOCATION('emp.txt') --文字檔的名字 15 )REJECT LIMIT UNLIMITED --發生幾次錯誤後,讀取動作將會中斷。UNLIMITED為無限制 16 ; SQL> DESC emp_ext Name Null? Type ----------------------------------------------------- ----------- ---------------------------------------- EMPID NUMBER(4) ENAME VARCHAR2(10) HIRE_DATE DATE SALARY NUMBER(6) SQL> SELECT * FROM emp_ext; EMPID ENAME HIRE_DATE SALARY ---------- -------------------- ---------------- ------------ 100 Frank 15-MAY-99 15000 101 Linda 16-SEP-04 20000 102 Gigi 23-FEB-05 7800 103 Wilson 06-SEP-07 10000 SQL> SELECT object_id,data_object_id,object_type FROM user_objects 2 WHERE object_name='EMP_EXT'; OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE ---------------- ------------------------- ------------------- 89052 TABLE --沒有data_object_id表示這個物件沒有相對的區段。 SQL> SELECT extent_id,blocks,bytes FROM user_extents WHERE segment_name='EMP_EXT'; no rows selected --真的沒有此區段存在 /*除使用符號分割Field外,也可以使用固定寬度的方式分割Field*/ [oracle@ELinux ~]$ cat /home/oracle/external_demo/emp1.txt 100 Frank 1999-05-15 15000 101 Linda 2004-09-16 20000 102 Gigi 2005-02-23 7800 103 Wilson 2007-09-06 10000 [oracle@ELinux ~]$ sqlplus frank/oracle SQL> CREATE TABLE emp1_ext 2 (empid NUMBER(4), 3 ename VARCHAR2(10), 4 hire_date DATE, 5 salary NUMBER(6)) 6 ORGANIZATION EXTERNAL 7 (TYPE ORACLE_LOADER 8 DEFAULT DIRECTORY external_ 9 ACCESS PARAMETERS 10 (RECORDS DELIMITED BY
  • 29.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 11 FIELDS(empid(1:4),ename(6:15),hire_date(17:26) DATEmask 'YYYY-MM-DD',salary(28:33)) 12 ) 13 LOCATION('emp1.txt') 14 )REJECT LIMIT UNLIMITED 15 ; /*使用固定寬度方式切割Record為Field,並對應到Column*/ SQL> SELECT * FROM emp_ext; EMPID ENAME HIRE_DATE SALARY ---------- -------------------- ---------------- ------------ 100 Frank 15-MAY-99 15000 101 Linda 16-SEP-04 20000 102 Gigi 23-FEB-05 7800 103 Wilson 06-SEP-07 10000 SQL> SELECT table_name,type_name FROM user_external_tables; --查詢有哪些外部表格 TABLE_NAME TYPE_NAME ------------------------------ ------------------------------ EMP_EXT ORACLE_LOADER EMP1_EXT ORACLE_LOADER Oracle 專屬格式檔 自 Oracle Database 10g 後,Oracle 增加一種新的外部表格格式,使用 Oracle 專屬格式檔案,而不是文 字檔。這種檔案是二進位格式,不過其中資訊是以 XML 格式存放。這種外部表格建立方式有兩種: UNLOAD(卸載)與 LOAD(載入)。 卸載:使用 CTAS(Create Table As Select)語法建立一個新的外部表格,為何稱為卸載?原因為這個外 部表格的資料來源為子查詢(SubQuery),而將資料存放到某個 Oracle 專屬格式檔案中,而不是資料庫中 的另一個表格。 SQL> DESC emp Name Null? Type ----------------------------------------------------- ----------- ---------------------------------------- EMPID NUMBER(3) ENAME VARCHAR2(20) HIRE_DATE DATE SALARY NUMBER(7) SQL> SELECT * FROM emp; EMPID ENAME HIRE_DATE SALARY ---------- -------------------- ---------------- ------------ 100 Frank 15-MAY-99 15000 101 Linda 16-SEP-04 20000 102 Gigi 23-FEB-05 7800 103 Wilson 06-SEP-07 10000 SQL> SELECT object_id,data_object_id,object_type FROM user_objects WHERE object_name='EMP'; OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE ---------------- ------------------------- ------------------- 89054 89054 TABLE --data_object_id不是空值,表示這是一個標準表格
  • 30.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 SQL> SELECT extent_id,blocks,bytesFROM user_extents WHERE segment_name='EMP'; EXTENT_ID BLOCKS BYTES ---------------- ------------ ---------- 0 8 65536 --此表格目前有一個擴充區塊 SQL> CREATE TABLE emp2_ext 2 ORGANIZATION EXTERNAL --宣告為外部表格 3 (TYPE ORACLE_DATAPUMP --檔案格式為Oracle專屬格式 4 DEFAULT DIRECTORY external_demo --使用external_demo這個目錄物件 5 LOCATION('emp2.dmp') --檔案的名字 6 ) 7 AS 8 SELECT * FROM emp; --卸載的資料來源 SQL> DESC emp2_ext Name Null? Type ----------------------------------------------------- ----------- ---------------------------------------- EMPID NUMBER(3) ENAME VARCHAR2(20) HIRE_DATE DATE SALARY NUMBER(7) SQL> SELECT * FROM emp2_ext; EMPID ENAME HIRE_DATE SALARY ---------- -------------------- ---------------- ------------ 100 Frank 15-MAY-99 15000 101 Linda 16-SEP-04 20000 102 Gigi 23-FEB-05 7800 103 Wilson 06-SEP-07 10000 SQL> SELECT object_id,data_object_id,object_type FROM user_objects 2 WHERE object_name='EMP2_EXT'; OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE ---------------- ------------------------- ------------------- 89055 TABLE --沒有data_object_id SQL> SELECT default_directory_name,type_name FROM user_external_tables 2 WHERE table_name='EMP2_EXT'; DEFAULT_DIRECTORY_NAME TYPE_NAME ----------------------------------------- ------------------------------ EXTERNAL_DEMO ORACLE_DATAPUMP --證實emp2_ext為外部表格,並且使用ORACLE_DATAPUMP界面存取檔案 SQL> SELECT directory_name,location FROM user_external_locations 2 WHERE table_name='EMP2_EXT'; --卸載後產生的檔案位置與名字 DIRECTORY_NAME LOCATION ---------------------------- ----------------- EXTERNAL_DEMO emp2.dmp SQL> SELECT directory_path FROM dba_directories WHERE directory_name='EXTERNAL_DEMO'; DIRECTORY_PATH --------------------------------------------------------------------------------------------------------------
  • 31.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 /home/oracle/external_demo --檔案系統上真實目錄 SQL> !ls-l /home/oracle/external_demo/emp2.dmp -rw-r----- 1 oracle oinstall 12288 Mar 27 17:00 /home/oracle/external_demo/emp2.dmp --建立外部表格所產生出的Oracle專屬格式檔案,其中存放著emp2_ext表格的內容 載入:載入為建立一個外部表格,能夠對應到之前產生 Oracle 專屬格式檔案的內容,讓資料庫的使用者 不須將檔案內容載入資料庫,即可透過外部表格查詢其內容。 [oracle@ELinux ~]$ pwd /home/oracle [oracle@ELinux ~]$ mkdir ext_dir [oracle@ELinux ~]$ cp /home/oracle/external_demo/emp2.dmp /home/oracle/ext_dir/emp3.dmp [oracle@ELinux ~]$ ls -l /home/oracle/ext_dir/emp3.dmp -rw-r----- 1 oracle oinstall 12288 3月 27 17:25 /home/oracle/ext_dir/emp3.dmp [oracle@ELinux ~]$ sqlplus / as sysdba --因為要建立新的目錄物件 SQL> CREATE DIRECTORY ext_dir AS '/home/oracle/ext_dir'; SQL> GRANT READ,WRITE ON DIRECTORY ext_dir TO frank; --授權讓Frank可以讀寫ext_dir目錄物件 SQL> CONNECT frank/oracle --切換身份為frank SQL> CREATE TABLE emp3_ext 2 (empid NUMBER(4), 3 ename VARCHAR2(10), 4 hire_date DATE, 5 salary NUMBER(6)) 6 ORGANIZATION EXTERNAL 7 (TYPE ORACLE_DATAPUMP 8 DEFAULT DIRECTORY ext_dir 9 LOCATION('emp3.dmp') 10 )REJECT LIMIT UNLIMITED; SQL> DESC emp3_ext Name Null? Type ----------------------------------------------------- ----------- ---------------------------------------- EMPID NUMBER(3) ENAME VARCHAR2(20) HIRE_DATE DATE SALARY NUMBER(7) SQL> SELECT * FROM emp3_ext; EMPID ENAME HIRE_DATE SALARY ------------- ---------------- ---------------- ------------ 100 Frank 15-MAY-99 15000 101 Linda 16-SEP-04 20000 102 Gigi 23-FEB-05 7800 103 Wilson 06-SEP-07 10000 當外部表格建立以後,資料庫管理者可以透過 DBA_EXTERNAL_TABLES 查詢外部表格的屬性設定, 也可利用 DBA_EXTERNAL_LOCATIONS 的內容得知資料來源檔案的名字。如果想要知道表格一般資訊 則如同其他表格型態一樣,查詢 DBA_TABLES 即可。 由於外部表格的唯讀特性,因此 ALTER TABLE 的操作僅允許以下幾種: 變更 Reject limit:ALTER TABLE emp_ext REJECT LIMIT 10; 變更 Project 限制:ALTER TABLE emp_ext PROJECT COLUMN REFERENCED; 變更 Access Parameters:ALTER TABLE emp_ext ACCESS PARAMETERS(FIELDS TERMINATED BY WHITESPACE);
  • 32.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 變更 Location:ALTER TABLEemp_ext LOCATION(`emp.dat’); 8.4 限制條件(Constraint) 資料完整性(Data Integrity)是指表格中的資料遵守商業規則(Business Rule),意即表格中的資料經過一 些過濾,確定符合事先設定的規範。例如人事規章中規定,每位公司員工必須有一個不能重複的員工編號 以供辨識,同時員工必須歸屬某個已經存在的部門等。為達到此目標,可以使用下列的方法: 前端應用程式檢查:在將資料輸入資料表之前,先使用前端應用程式對資料進行檢查,確定符合資料符合 特定規則,才能存入資料表中。 後端資料庫觸發器檢查:在後端資料庫撰寫觸發器,當資料新增、異動、刪除時便會觸發資料檢查程序, 唯有符合規則的資料才能成功地新增、異動與刪除。 然而前兩種方法都是透過撰寫程式碼達到所需的功能,然而對資料庫管理人員來說,撰寫相關的程式可能 過於困難或麻煩。所以可以先使用資料庫內建的限制條件(Constraint)進行基本、簡單的檢查,當限制條 件做不到所要的功能時,才使用前端應用程式或後端資料庫的觸發器進行更複雜的處理與檢查。 使用限制條件有以下好處: 僅需要宣告即可,不須撰寫複雜的程式碼。 效率較程式碼來的好,因為由 Oracle 核心完成。 限制條件設定在資料庫端,所以任何前端應用程式都會受到規範,不會有遺漏。 8.4.1 限制條件的種類 Oracle 資料庫提供五種限制條件,讓使用者可以依需求選擇合適的限制。以下針對這五種限制條件進行 說明: NOT NULL(不可為空值):NOT NULL 會限制欄位值不可以為空值。不過此種限制條件僅能針對單一欄 位。 UNIQUE KEY(唯一索引鍵):UNIQUE KEY 限制條件的欄位值必須是唯一,也就是欄位值不可與其他資 料列的欄位值重複,此種限制條件可以針對單一欄位或多個欄位所組成的複合值。 PRIMRAY KEY(主索引鍵):其實 PRIMARY KEY 是由 NOT NULL 與 UNIQUE KEY 所共同組成的,因 此欄位值不能為空值,也不能重覆。同時 PRIMARY KEY 限制可以針對一個欄位值或由多個欄位所組成 的複合值,不過一個表格僅能有一個 PRIMARY KEY 限制。若有其他欄位也需要如 PRIMARY KEY 限制 一般的功能,只能使用 NOT NULL 與 UNIQUE KEY 兩個限制結合來達到該要求。 FOREIGN KEY(外來索引鍵):FOREIGN KEY 這種限制條件是要求欄位值必須參考其他欄位中已有的內 容,不能隨心所欲地新增或異動欄位值,不過被參考的欄位必須已經有 PRIMARY KEY 或 UNIQUE KEY 限制條件存在。同時一般稱 FOREIGN KEY 所在的表格為子表格(CHILD TABLE),而被參考的表格為父 表格(PARENT TABLE)。 CHECK(檢查):此種限制是要求欄位值必須讓某個條件式為真(TRUE)後,才允許欄位新增或異動,此種 限制只能針對單一欄位。條件式的範例如下:CHECK(Salary>10000),此限制要求 Salary 欄位值必須 大於 10000。 宣告限制條件可以分成欄位層次(INLINE CONSTRAINT)與表格層次(OUT OF LINE CONSTRAINT)。 NOT NULL 只能使用欄位層次,但若為複合欄限制,則必須採用表格層次。同時採用欄位層次宣告限制 時,不需要在限制條件後加上欄位名稱,但表格層次則必須加上欄位名稱。此外限制條件的名字,可以藉 由 CONSTRAINT 參數設定,若沒有指定,則由 Oracle 伺服器自動產生-(SYS_Cnnnnn,nnnnn 為序列 號)。 SQL> CREATE TABLE frank.con_t1 2 (a NUMBER PRIMARY KEY, --欄位宣告後,立刻接著限制條件的宣告,此為欄位層次 3 b VARCHAR2(10), 4 c DATE,
  • 33.
    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)表格即可。
  • 58.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 當表格被刪除時,此表格的定義與所有的擴充區塊將一併被刪除。當然這個表格上的索引、限制條件及觸 發器也會一起被刪除。因為刪除表格並沒有一筆一筆地將資料列刪除,而是將所使用的擴充區塊釋放,當 做表格空間的可用擴充區塊,所以不會有相對的重做項目與還原資料產生。 --刪除表格,但是可能可以利用Flashback Drop救回。 SQL>DROP TABLEfrank.t_col_opers; --若此表格有被其他表格使用外鍵限制參考,必須使用cascade constraints,同時將子表格的外鍵限制刪除。 SQL>DROP TABLE frank.t_col_opers CASCADE CONSTRAINTS; --刪除表格,並且將表格由Recyclebin中清除,所以不能利用Flashback Drop方式救回此表格。 SQL>DROP TABLE frank.t_col_opers PURGE; 回溯表格刪除(Flashback Drop) 從 Oracle Database 10g 開始,表格的刪除不再是不可復原的操作。因為 Oracle Database 10g 使用資 源回收筒(Recyclebin)機制,讓資料庫管理者意外刪除重要表格時,可能有機會挽救這個錯誤的刪除。 當 RECYCELBIN 參數值為 ON(預設值),資料庫管理者刪除某個表格時,資料庫並不是立刻刪除該表格 的定義、刪除表格所使用的區段(也就是釋放所使用的擴充區塊),刪除表格上的索引、限制條件及觸發器。 而是將表格更名為以 BIN$前綴的名字,相關的索引與限制條件也是更名為 BIN$的名字,同時這個表格及 其上的索引所擁有的擴充區塊將被歸類為可用的擴充區塊(Free Extent)。這些 BIN$為前綴字的物件邏輯 上被劃分在資源回收筒中,但是實際上還是存在於原本的表格空間之中。
  • 59.
  • 60.
    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內容
  • 63.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 Extent Header:: spare1:0 spare2: 0 #extents: 1 #blocks: 7 last map 0x00000000 #maps: 0 offset: 4128 Highwater:: 0x01000472 ext#: 0 blk#: 0 ext size: 7 #blocks in seg. hdr's freelists: 0 #blocks below: 0 /*High Water Mark的位置改為第1個Extent的第0個Block,表示此Segment沒有任何使用過的Block存在,當 然也不會有任何資料列存在。*/ --------------------------------------------------------------------------------------------------------------------- SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T1'); SQL> SELECT num_rows,blocks FROM dba_tables WHERE owner='FRANK' AND table_name='T1'; NUM_ROWS BLOCKS ------------------ --------------- 0 0 8.5.4 表格重組(Table Reorganization) 表格重組是許多資料庫管理者喜歡進行的資料庫維護操作之一,另一個則是索引重組。可是許多資料庫管 理者並不知道表格重組的原因與用途,只是人云亦云地進行表格重組與索引重組。如果資料庫的可用性 (Availability)不會受到影響,那麼盲目地進行這些工作也不會有問題。但是若因為料庫必須保持 24x7 的 可用性,無法在離峰時期進行此類操作。這時表格重組或索引重組的效益就必須被嚴格檢視,而且必須大 於重建的成本,才有必要執行這些資料庫維護動作。 表格重組的用途 當表格經過多次 DML 操作後,每個的表格區塊都可能會有些資料列被刪除,而多出許多的可用空間。如 果表格往後還會有新的資料載入,則這些可用空間只是暫時閒置,往後新增的資料列還是可以重新使用這 些空間。也就是說空間浪費只是暫時的現象,這種情況便不需要進行表格重組。 如果因為表格都一直沒有新增的資料或新增的資料量遠小於刪除的資料量,也可能新增的資料使用附加 (Append)的方式等,造成許多的表格區塊有大量的可用空間。這時便可以說表格面臨表格片段(Table Fragmentation)的問題,也就是空間浪費的問題。 但是這種情況對效能來說是否有不良的影響?首先如果存取這個表格的 SQL 敘述句絕大多數使用索引掃 描(Index Scan),這時便沒有效能上的問題。因為 Oracle 資料庫先使用索引,找到資料列的資料列辨識 碼,在使用資料列辨識碼讀取該筆資料列。但是因為 Oracle 資料庫的最小存取單位是一個資料區塊,所 以不管資料區塊中目前有幾筆資料列,還是要讀取整個區塊。雖然表格片段並不影響索引存取的效率,但 是表格空間的浪費可能還是需要注意。 但是如果全表格掃描存取表格,則對效能可能有重大的衝擊。因為全表格掃描是將表格所有用過的資料區 塊都讀到緩衝區快取後,才開始篩選符合條件的資料列。表格的總資料區塊數量越多,則讀取的資料區塊 個數也越多,當然效能也越差。這時就應該適時地進行表格重組的操作,也提昇 SQL 敘述句的效能。當 然建立索引也是一個可以選擇的解決方案。 表格移動(Table Move) Oracle Database 8i 開始提供表格移動的功能,這個功能本來的用途是讓資料庫管理者可以將表格的區 段(Segment)搬移到另一個表格空間。但是因為搬移表格區段時,並不是真正的將區段搬移到另一個表格 空間。而是在該表格空間建立一個新的區段,將原本的區段當做資料來源,使用新增(Insert)操作將區段 的所有資料列都複製到新區段。然後修改資料辭典將新區段設定為該表格所使用的區段,最後刪除舊的區 段回收空間。 可是因為搬移區段所使用的方式為新增操作,所以新區段的資料區塊都會塞滿資料列,只留下 PCTFREE 所要求的空間比率給未來的更新使用。因此新區段的大小應該會比舊區段來得小,所以可以達到表格重組 的效果。此外表格搬移也會解決資料列遷移的問題,同時若搬移到較之前所使用的表格空間區塊大小更大 的表格空間,也可能一同解決資料鏈結的問題。 因為使用舊的區段當做資料來源,因此在表格搬移的期間,表格將被鎖定,而且鎖定的模式為獨佔模式 (Exclusive Mode)。所以在表格搬移的過程中,表格不能進行其他人的 DML 或 DDL 操作。而且表格搬 移的過程中,需要約當於舊區段大小的額外空間,因為舊的區段與新的區段同時存在。同時因為舊資料列
  • 64.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 實際上已經被刪除,而新資料列則儲存在不同的區段。因此在表格搬移前所建立的索引,其中所記錄的資 料列 id 已經無法找到資料列,所以索引的狀態變為無法使用(Unusable)。因此必須重建索引,讓索引所 儲存的資料列id 更新為新資料列的位置,這樣才能讓索引的狀態變為生效(Valid)。 --建立範例環境 SQL> CONNECT / AS SYSDBA SQL> CREATE TABLE frank.t2 AS SELECT * FROM dba_tables; SQL> CREATE INDEX frank.t2_name_idx ON frank.t2(table_name); SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2'); --目前表格所在的表格空間為USERS,以及共有2537筆資料列與使用87個資料區塊。 SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T2'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ----------------- ----------- T2 USERS 2537 87 --目前索引的狀態為有效 SQL> SELECT index_name,status FROM dba_indexes 2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX'; INDEX_NAME STATUS ------------------------------ ----------- T2_NAME_IDX VALID --表格搬移前,表格的物件編號為78698,以及區段編號為78698 SQL> SELECT object_name,object_id,data_object_id FROM dba_objects 2 WHERE owner='FRANK' AND object_name='T2' AND object_type='TABLE'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID ------------------------------ ---------------- ------------------------- T2 78698 78698 --目前區段所使用的擴充區塊數量為11個,共有88個資料區塊。 SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents 2 WHERE owner='FRANK' AND segment_name='T2'; EXTENT_ID FILE_ID BLOCK_ID BLOCKS ---------------- ----------- --------------- ------------ 0 4 6753 8 1 4 6761 8 2 4 9921 8 3 4 9929 8 4 4 9937 8 5 4 9945 8 6 4 9953 8 7 4 9961 8 8 4 9969 8 9 4 9977 8 10 4 9985 8 11 rows selected. --表格搬移前資料列的資料列id SQL> SELECT rowid,owner,table_name FROM frank.t2 2 WHERE owner='HR' AND table_name='EMPLOYEES';
  • 65.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 ROWID OWNER TABLE_NAME --------------------------------------------------------------- ------------------------------ AAATNqAAEAAACcGAAd HR EMPLOYEES --進行測試的資料刪除,造成資料區塊有部份空間浪費 SQL> DELETE frank.t2 WHERE owner IN ('SYS','SYSTEM'); SQL> COMMIT; SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2'); /*雖然資料列筆數減少為1513筆,但所使用的資料區塊數量與資料列筆數2537筆相同。 因此有部份資料區塊有空間浪費的現象。*/ SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T2'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ----------------- ------------ T2 USERS 1513 87 --使用表格搬移將表格的區段搬移到example表格空間 SQL> ALTER TABLE frank.t2 MOVE TABLESPACE example; /*若使用ALTER TABLE frank.t2 MOVE,則新的區段建立在舊區段所在的表格空間。因此除新區段所在的表 格空間不同外,其餘的動作則與MOVE TABLESPACE相同。*/ --表格搬移後,表格的物件編號不變依然為78698,但所使用的區段,已經變為區段編號為78702的區段 SQL> SELECT object_name,object_id,data_object_id FROM dba_objects 2 WHERE owner='FRANK' AND object_name='T2' AND object_type='TABLE'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID ------------------------------ ---------------- ------------------------- T2 78698 78702 /*新區段擁有7個擴充區段,共56個資料區塊。新區段比舊區段小32個資料區塊,表示達到表格重組的效果。 同時擴充區塊的來源已經改為datafile# 5。*/ SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents 2 WHERE owner='FRANK' AND segment_name='T2'; EXTENT_ID FILE_ID BLOCK_ID BLOCKS ---------------- ----------- ------------- ------------ 0 5 465 8 1 5 473 8 2 5 481 8 3 5 489 8 4 5 497 8 5 5 505 8 6 5 513 8 7 rows selected. --雖然資料列的內容相同,但是資料列的資料列id已經完全不同。因此索引需要重建。 SQL> SELECT rowid,owner,table_name FROM frank.t2 2 WHERE owner='HR' AND table_name='EMPLOYEES'; ROWID OWNER TABLE_NAME -------------------------------- ------------------------------ ------------------------------ AAATNsAAFAAAAIFAAY HR EMPLOYEES --索引狀態已經變成不可用(Unusable),因為索引項目的資料列id與表格重組後的資料列id不同。 SQL> SELECT index_name,status FROM dba_indexes 2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX';
  • 66.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 INDEX_NAME STATUS ------------------------------ --------------- T2_NAME_IDXUNUSABLE --索引重建 SQL> ALTER INDEX frank.t2_name_idx REBUILD; --索引重建後,索引的狀態變成有效(Valid)。 SQL> SELECT index_name,status FROM dba_indexes 2 WHERE owner='FRANK' AND index_name='T2_NAME_IDX'; INDEX_NAME STATUS ------------------------------ ----------- T2_NAME_IDX VALID SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T2'); --同樣的資料筆數(1513),重組後只需要使用54個資料區塊,而且表格的區段已經搬移到example表格空間。 SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T2'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ------------------ ----------- T2 EXAMPLE 1513 54 縮小區段(Shrink Space) Oracle Database 10g 提供一個新功能︰縮小區段,這個功能用來縮小區段所用到的空間,並且移動高水 位標記,回收位於新的高水位標記以上的區塊。 分成兩個階段,首先為 SHRINK SPACE COMPACT。這個操作需要對表格加上表格層次的共用鎖定 (TM:Row Exclusive),並對搬移資料列加上獨佔鎖定(Exclusive)。然後將表格的資料列盡可能地向區段 標頭集中,使用的方法就是使用一組新增、刪除資料列操作。先新增新的資料列,然後刪除舊的資料列但 新增的資料列儘可能靠近區段標頭。資料庫管理者可以線上重組表格,而不會阻擋其他人對此表格進行 DML 操作。同時縮小區段不會建立新的區段,所以可以減少表格重組過程中額外的空間需求。 Shrink space:取得表格獨佔鎖定,將高水位標記移動到目前資料區塊有資料列的最後一個資料區塊的位 置。之後釋放新高水位標記以上的資料區塊,因此表格空間將標示那些回收的擴充區塊為可用的擴充區塊。 --建立範例表格與索引 SQL> CREATE TABLE frank.t3 AS SELECT * FROM dba_objects WHERE object_id<=5000; SQL> CREATE INDEX frank.t3_id_idx ON frank.t3(object_id); SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3'); SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T3'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ----------------- ------------ T3 USERS 4928 71 SQL> SELECT index_name,status FROM dba_indexes 2 WHERE owner='FRANK' AND index_name='T3_ID_IDX'; INDEX_NAME STATUS ------------------------------- -----------
  • 67.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 T3_ID_IDX VALID SQL> SELECTextent_id,file_id,block_id,blocks FROM dba_extents 2 WHERE owner='FRANK' AND segment_name='T3'; EXTENT_ID FILE_ID BLOCK_ID BLOCKS ---------------- ----------- --------------- ------------ 0 4 10889 8 1 4 10897 8 2 4 11273 8 3 4 11281 8 4 4 11289 8 5 4 11297 8 6 4 11545 8 7 4 11553 8 8 4 11561 8 9 rows selected. --找出object_id為4999的資料列的資料列id SQL> SELECT rowid,object_id,object_name FROM frank.t3 WHERE object_id=4999; ROWID OBJECT_ID OBJECT_NAME -------------------------------- ---------------- ------------------------------ AAATNyAAEAAAC0vABI 4999 DBA_SNAPSHOTS --object_id為4999的資料列所在的資料區塊為11567,資料列為該區塊的第72筆資料列 SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_num, 2 dbms_rowid.rowid_row_number(rowid) row_num 3 FROM frank.t3 WHERE object_id=4999; BLOCK_NUM ROW_NUM ------------------ --------------- 11567 72 --縮小區段之前的表格編號為78706,區段編號為78706 SQL> SELECT object_name,object_id,data_object_id FROM dba_objects 2 WHERE owner='FRANK' AND object_name='T3' AND object_type='TABLE'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID ------------------------------ ---------------- ------------------------- T3 78706 78706 --進行縮小區段前,表格必須啟用資料列移動(Row Movement)的功能。 SQL> ALTER TABLE frank.t3 ENABLE ROW MOVEMENT; --進行測試的資料刪除,造成資料區塊有部份空間浪費 SQL> DELETE frank.t3 WHERE MOD(object_id,2)=0; SQL> COMMIT; SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3'); /*經過資料異動後,雖然資料筆數只剩下2460,但所使用的空間還是需要71個資料區塊。 可以考慮使用縮小區段將表格重組,並釋放多餘的資料區塊。*/ SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T3'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ----------------- ------------
  • 68.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 T3 USERS 246071 --只搬移資料列,但不重設高水位標記 SQL> ALTER TABLE frank.t3 SHRINK SPACE COMPACT; --縮小區段並不會建立一個新的區段,所以縮小區段後,區段編號並沒有改變。 SQL> SELECT object_name,object_id,data_object_id FROM dba_objects 2 WHERE owner='FRANK' AND object_name='T3' AND object_type='TABLE'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID ------------------------------ ---------------- ------------------------- T3 78706 78706 --進行縮小區段前,表格必須啟用資料列移動(Row Movement --object_id為4999的資料列的資料列id已經與進行縮小區段之前的資料列id不同。 SQL> SELECT rowid,object_id,object_name FROM frank.t3 WHERE object_id=4999; ROWID OBJECT_ID OBJECT_NAME --------------------------------- ---------------- ------------------------------ AAATNyAAEAAACqMABI 4999 DBA_SNAPSHOTS /*縮小區段之前object_id為4999的資料列位於編號11567的資料區塊的第72筆資料列 縮小區段之前object_id為4999的資料列位於編號10892的資料區塊的第72筆資料列*/ SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_num, 2 dbms_rowid.rowid_row_number(rowid) row_num 3 FROM frank.t3 WHERE object_id=4999; BLOCK_NUM ROW_NUM ------------------ --------------- 10892 72 --因為縮小區段使用新增/刪除資料列的技巧來搬移資料列,因此索引的內容也一同被維護。 SQL> SELECT index_name,status FROM dba_indexes 2 WHERE owner='FRANK' AND index_name='T3_ID_IDX'; INDEX_NAME STATUS ------------------------------ ------------ T3_ID_IDX VALID --縮小區段並沒有建立新的區段,所以擴充區塊並未改變。 SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents 2 WHERE owner='FRANK' AND segment_name='T3'; EXTENT_ID FILE_ID BLOCK_ID BLOCKS ---------------- ----------- --------------- ------------ 0 4 10889 8 1 4 10897 8 2 4 11273 8 3 4 11281 8 4 4 11289 8 5 4 11297 8 6 4 11545 8 7 4 11553 8 8 4 11561 8 9 rows selected. --SHRINK SPACE將會搬移資料列,並會重新設定高水位標記,與釋放高於高水位標記以上的資料區塊。
  • 69.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 SQL> ALTER TABLEfrank.t3 SHRINK SPACE; --收集表格的統計資料 SQL> EXECUTE dbms_stats.gather_table_stats('FRANK','T3'); --經過SHRINK SPACE後,所使用的區塊個數減為33個,因此達到表格重組的效過。 SQL> SELECT table_name,tablespace_name,num_rows,blocks FROM dba_tables 2 WHERE owner='FRANK' AND table_name='T3'; TABLE_NAME TABLESPACE_NAME NUM_ROWS BLOCKS ------------------------------ ------------------------------ ----------------- ------------ T3 USERS 2460 33 --區段所擁有的擴充區塊個數也減少為5個,共40個資料區塊 SQL> SELECT extent_id,file_id,block_id,blocks FROM dba_extents 2 WHERE owner='FRANK' AND segment_name='T3'; EXTENT_ID FILE_ID BLOCK_ID BLOCKS ---------------- ----------- --------------- ------------ 0 4 10889 8 1 4 10897 8 2 4 11273 8 3 4 11281 8 4 4 11289 8 8.5.5 唯讀表格(Read-Only Table) 從 Oracle Database 11g 開始,可以將表格的狀態設定為唯讀,禁止對這個表格的異動。進行此項操作 之前,使用者必須擁有 ALTER TABLE 權限(針對自己擁有的表格)或 ALTER ANY TABLE 權限(針對任 何綱要下的表格)。 SQL> SELECT table_name,read_only FROM user_tables WHERE table_name='T1'; TABLE_NAME REA ------------------------------------- ------- T1 NO /*目前表格T1可以被異動*/ SQL> SELECT COUNT(*) FROM t1; COUNT(*) --------------- 1 SQL> DELETE t1; /*成功刪除所有的ROWS*/ 1 row deleted. SQL> SELECT COUNT(*) FROM t1; COUNT(*) --------------- 0 SQL> ROLLBACK; /*到回交易*/ SQL> ALTER TABLE t1 READ ONLY; /*將表格設定為唯讀*/
  • 70.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 SQL> DELETE T1; DELETET1 * ERROR at line 1: ORA-12081: update operation not allowed on table "SYS"."T1" SQL> SELECT table_name,read_only FROM user_tables WHERE table_name='T1'; TABLE_NAME REA ------------------------------------- ------- T1 YES SQL> ALTER TABLE t1 READ WRITE ; /*將表格設定為可異動*/ 8.5.6 表格回溯(Flashback Table) 表格回溯可以將一個表格或多個表格的內容,回溯到之前某個時間點的內容。而且與該表格相關的索引、 限制(Constraint)也將一同回溯。資料庫管理者也可以選擇啟動觸發器(Trigger),以維護資料的完整性 (Integrity)及一致性。 FLASHBACK TABLE table_name TO { { SCN | TIMESTAMP } expr [ { ENABLE | DISABLE } TRIGGER S ] };
  • 71.
    Chapter 8 Table Oracle Database 11g 資料庫管理入門 8.6 常用的資料辭典視觀表 結論 整個資料庫中,個數最多、佔用空間最大的區段就是表格,如果資料庫管理者對表格還不甚了解,那麼要 如何進行進一步的效能調整。然而 Oracle資料庫所提供的表格型態不是只有本章所提及的標準表格、暫 時表格與外部表格而已,還有分割區表格(Partitioned Table)、叢集表格(Cluster Table)與索引組織表格 (Index Organized Table)等。不過這些表格型態通常用在特殊的環境下,在此就不多做說明。