Oracle编程入门经典 第7章 表
表是在行和列中存儲數據的基本結構。而且,就如同Oracle向數據庫整體增加特性一樣,隨著時間失衡,它也增強了表的概念,以適應更加復雜的應用要求。在本章中,我們將要討論:
- Oracle中最常用的表類型,以及用戶為什么要使用各種類型。
- 用戶在Oracle中將會遇到的表特性,它們可以影響表的操作方式。
- 怎樣ALTER、DROP和TRUNCATE表
7.1????????? 介紹Oracle中的表
在Oracle中,存儲數據從沒有如此容易或如此高效。除了對SQL優化器進行了改進外,數據庫內核、數據庫管理配置選項等也都得到了強化。Oracle已經發布了新類型的表,來適應各種類型的數據存儲、數據訪問以及性能要求。
對于所有類型的表,Oracle都允許開發人員和管理員規定各種表屬性,它們會確定如下內容:
- 哪一個表空間包含表
- Oracle怎樣將表物理存儲在磁盤上
- 當從磁盤讀取表數據的時候,Oracle怎樣將它們與內存映射
- Oracle怎樣控制表上特定操作的日志
新的表類型還能夠在開發和管理解決方案的時候節省用戶時間。隨著用戶掌握不同類型的可用表,用戶將會發現這些表可以滿足那些使用堆存儲的標準表之外的要求。這些要求過去通常要由處理問題的開發人員/或者管理員使用其它方式解決,但是因為Oracle的新表類型將會處理這些問題,所以就可以省去這些工作。
7.2????????? 表類型
7.2.1?? 堆表
最基本的表類型就是堆表(Heap table)。術語堆是指數據在磁盤上隨機存儲的方式。一般來說,Oracle在將行寫入數據塊的時候不會考慮其它的行的存儲位置。當向堆表插入行的時候,數據庫會將數據寫入第一個具有足夠自由空間的段。當更新和刪除行的時候,就會為新的插入提供可用空間。
為了展示堆表,我們將要模型化一個主要涉及信息技術領域的公司的培訓和教育部門。
試驗:建立堆組織的表
(1)????? 我們將要建立的第一個是SUBJECTS,它給出了培訓部門將要教授的課程類別。
SQL> create table subjects(2 subject_id number not null,3 subject_name varchar2(30) not null,4 description varchar2(4000)5 )6 tablespace users7 / 表已創建。就如用戶所見,我們已經建立了3列,名稱分別為SUBJECT_ID、SUBJECT_NAME、DESCRIPTION。這些列分別具有數據類型NUMBER、VARCHAR2(30)和VARCHAR2(4000)。注意,要使用NOT NULL子句來確保SUBJECT_ID和SUBJECT_ANME具有值。
(2)????? 現在已經建立SUBJECTS表,我們將要使用ALTER TABLE命令,使SUBJECT_ID列成為主鍵:
SQL> alter table subjects2 add constraint pk_subjects3 primary key(subject_id)4 / 表已更改。?
(3)????? 建立了SUBJECTS表之后,我們將會繼續建立一個COURSES的子表,它將會存儲SUBJECTS表中各個學科的課程。
SQL> create table courses(2 course_id number not null,3 course_name varchar2(60) not null,4 subject_id number not null,5 duration number(2),6 skill_lvl varchar2(12) not null7 )8 tablespace users9 / 表已創建。在這個例子中,我們已經建立了COURSES列的數據類型。
(4)????? 現在我們需要定義約束。
SQL> alter table courses2 add constraint pk_courses3 primary key(course_id)4 / 表已更改。其次,我們希望COURSES的SUBJECT_ID列能夠成為引用SUBJECTS表的外鍵:
SQL> alter table courses2 add constraint fk_course_subj3 foreign key(subject_id) references subjects(subject_id)4 / 表已更改。最后,我們想要在SKILL_LVL列上實現一個檢查約束,以確保各行中這個列的唯一可能值是BEGINNER、INTERMEDIATE或ADVANCED;
SQL> alter table courses2 add constraint ck_level check(3 skill_lvl in('BEGINNER','INTERMEDIATE','ADVANCED')4 )5 / 表已更改。工作原理
這里向用戶提供了建立2個彼此相關聯的堆組織表的示例。我們使用TABLESPACE子句來確保將表放到正確的表空間中,并且使用約束來強制一些與表中存儲數據相關聯的業務規則。
7.2.2????????? 外部表
在Oracle 9i中新出現的外部表(external tables)是在數據庫以外的文件系統上存儲的只讀表。
在Oracle 9i以前,使用操作系統上的普通文件中存儲數據的唯一方式就是通過SQL*Loader工具將其載入數據庫,或者相應于普通文件中的數據使用INSERT,手工建立堆組織表。
通過使用外部表,就無須將數據復制到數據庫中,并且強制更新,我們可以讓數據保留在普通文件中,并且允許數據庫對其進行實地讀取。這種方法,外部應用可以采用它認為合適的方法更新數據,而且也不用調用SQL*Loader執行數據載入操作。
試驗:建立和使用外部表
在以下的示例中,我們將要根據包含有逗號分隔值的文件文件(teacher.csv,通過建立Excel表格另存為teacher.csv,并放在C:\),建立一個示例外部表。
(1)????? 為了建立外部表,Oracle需要知道文件在操作系統上的位置。這可以使用目錄對象來實現,這個數據庫對象可以作為服務器文件系統上目錄的別名。為了建立指向我們數據文件位置的目錄,用戶需要CREATE ANY DIRECTORY特權,這意味著要使用DBA賬號,或者要為用戶用于這個示例的用戶賬號賦予這個權限。
(具體賦予權限語句為grant create any directory to scott with admin option;)
SQL> create directory ext_data_files2 as 'C:\'3 / 目錄已創建。用戶需要根據自己放置teacher.csv文件的位置修改這個路徑,這里是把它放在C:\
(2)????? 現在,我們要為外部表建立表定義。Oracle所存儲的唯一信息就是外部表的元數據。數據庫要負責在提交對外部表的查詢的時候從外部數據源中獲取數據:
SQL> create table teachers_ext(2 first_name varchar2(15),3 last_name varchar2(15),4 phone_number varchar2(12)5 )6 organization external(7 type oracle_loader8 default directory ext_data_files9 access parameters(10 fields terminated by ',')11 location('teacher.csv')12 )13 reject limit unlimited14 / 表已創建。(3)????? 既然已經建立了表,那么我們就可以在其上執行SQL查詢,從Oracle數據庫請求操作系統上存儲的文件的信息:
SQL> select first_name,last_name,phone_number2 from teachers_ext;FIRST_NAME LAST_NAME PHONE_NUMBER --------------- --------------- ------------ Jean Miller 123-0107 Jean Miller 123-0108如果用戶打開數據文件,用戶就會發現2條記錄。用戶可以注意到,這個文件沒有發現任何變化或者改動。Oracle只是會根據外部表TEACHERS_EXT的定義原樣讀取文件,讓數據庫可以使用數據。
?
以上是TEACHERS_EXT表的操作日志。
工作原理
CREATE TABLE 語句要比標準的堆組織表更復雜一些,所以我們將會逐步分析完整的DDL語句。
前5行與其它的CREATE TABLE 語句大體相同:
SQL> create table teachers_ext(2 first_name varchar2(15),3 last_name varchar2(15),4 phone_number varchar2(12)5 )在將數據文件的內容與表中的列進行了映射之后,接下來,我們就要使用ORGANIZATION EXTERNAL子句指出正在建立的表的外部表。
Oracle為外部表主要提供了兩種驅動
1. the loader access driver, or ORACLE_LOADER
2. the import/export access driver, or ORACLE_INTERNAL
6 organization external(7 type oracle_loader在CREATE TABLE語句的下一部分中,要使用DEFAULT_DIRECTORY和LOCATION屬性規定外部文件在操作系統的文件系統上的位置:
8 default directory ext_data_files9 access parameters(10 fields terminated by ',')11 location('teacher.csv')12 )CREATE TABLE 語句的最后一個子句是REJECT LIMIT子句:
13 reject limit unlimited 14 /這個子句會告訴Oracle在將源數據轉換為表定義中映射的列數據類型期間,數據庫允許多少錯誤。如果用戶在執行一個查詢,而Oracle遇到了超過這個轉換數量的錯誤,那么查詢就會失敗。REJECT LIMIT的默認值是0。UNLIMITED關鍵詞表明將會忽略轉換錯誤,查詢決不會失敗。在最糟糕的情況下,如果外部數據文件中的所有記錄都由于轉換錯誤而失敗,那么查詢這個表就會只返回0行。
1?????????? Type(或者訪問驅動器類型)
外部表的訪問驅動器(access driver)是一種工具,它可以將數據從它們最初的格式轉換為可以向服務器提供的方式。換句話說,表的建立者/所有者能夠使用訪問驅動器發動源文件,以便它們能夠被數據庫讀取。
Oracle附帶了默認的訪問驅動器ORACLE_LOADER,然而用戶應該注意到,還可以構建訪問驅動器去支持其它外部表類型。
2?????????? DEFAULT DIRECTORY
當用戶建立外部表的時候,用戶實際只是在數據庫中存儲了元數據(一般認為,所謂元數據是關于數據的數據,或關于數據的結構化的數據)。數據本身還是在數據庫之外存儲。當定義外部表的時候,用戶將要定義一個默認目錄,告訴Oracle外部表文件位于文件系統的何處。
對于我們已經建立的默認目錄EXT_DATA_FILES,我們可以進行如下所示授權:
SQL> grant all on directory ext_data_files to scott2 / 授權成功。這可以讓SCOTT有能力建立外部表,并且在表定義中使用EXT_DATA_FILES目錄對象。
3?????????? BADFILE和NOBADFILE
當讀取用戶的外部表時,數據庫可能會遇到數據類型轉換錯誤,不能夠將源文件轉換成數據庫中為外部表定義的列。如果用戶在CREATE TABLE語句中使用如下語法,那么所有不能轉換的數據值都會寫入BADFILE中:
SQL> create table teachers_ext(2 first_name varchar2(15),3 last_name varchar2(15),4 phone_number varchar2(12)5 )6 organization external(7 type oracle_loader8 default directory ext_data_files9 access parameters(10 records delimited by newline11 badfile ext_data_files:'teacher.bad'12 fields terminated by ',')13 location('teacher.csv')14 )15 reject limit unlimited16 / 表已創建。 SQL> select first_name,last_name,phone_number from teachers_ext; FIRST_NAME LAST_NAME PHONE_NUMBER --------------- --------------- ------------ Jean Miller 123-0107 Jean Miller 123-0108 Jean Mille12r 123-0109type oracle_loade-——指定外部表的訪問方式
records delimited by newline——以新的一行分隔記錄
badfile ext_data_files:'teacher.bad'——讀取記錄轉換失敗存取到.bad文件里
將第3條記錄更改為:
Jean,Mill12341234321212e12r,123-0109?
因為Mill12341234321212e12r太過長,所以只顯示2條記錄:
SQL> select first_name,last_name,phone_number from teachers_ext; FIRST_NAME LAST_NAME PHONE_NUMBER --------------- --------------- ------------ Jean Miller 123-0107 Jean Miller 123-0108?
轉換為元數據失敗的記錄存放在.bad文件中
BADFILE非常有用,因為它們可以為表的所有者提供一個可供分析的文件,找到發生錯誤的記錄。對于在他們的應用中使用了外部表的管理員,使用BADFILE也會非常有助于調整源數據或者表定義,以確保源文件中的所有數據都可以由Oracle讀取。
如果沒有調整數據的原因,或者這些轉換錯誤無關緊要,那么就可以在建立表的時候使用NOBADFILE子句。這將會讓Oracle完全不理會數據類型轉換錯誤,只是簡單忽略錯誤記錄。NOBADFILE可以在CREATE TABLE命令中規定,如下所示:
SQL> create table teachers_ext(2 first_name varchar2(15),3 last_name varchar2(15),4 phone_number varchar2(12)5 )6 organization external(7 type oracle_loader8 default directory ext_data_files9 access parameters(10 nobadfile11 fields terminated by ',')12 location('teacher.csv')13 )14 reject limit unlimited15 / 表已創建。?
如果沒有在CREATE TABLE語句中規定BADFILE和NOBADFILE,在默認情況下,Oracle就會使用表名稱和.BAD擴展名為主,在數據文件所處的目錄建立一個BADFILE,其名稱和日志名相同。
?
4?????????? LOGFILE和NOLOGFILE
LOGFILE可以用于寫入在Oracle嘗試訪問數據文件的時候所遇到錯誤的消息。這在設置新的外部文件表的時候非常有用,因為在第一次建立表的時候,外部表中會經常發生錯誤。如果操作系統限制Oracle讀取文件,或者用于實例的源數據文件沒有存在,那么就會將這些錯誤記錄到日志文件中。
當應該忽略訪問外部數據源的時候所遇到的錯誤時,就要使用NOLOGFILE。如果規定了這個子句,Oracle就不會將錯寫入任何日志文件。
如果CREATE TABLE語句沒有規定LOGFILE或者NOLOGFIEL,Oracle就會在默認情況下建立一個LOGFILE。LOGFILE的名稱將會是<table name>.LOG.
簡而言之,外部表是將外部數據文件手工載入數據庫的絕佳替代方法。在Oracle 9i的第一次發布中,外部表還是吟詩的,只能夠用于查詢數據。Oracle沒有提供內部的方式去更新或者刪除這些表中的記錄。另外,Oracle也不能在外部表上建立索引。這對于用戶表的需求可能會有一些限制。
索引能力的缺失意味著SELECT語句中Oracle總是要完全搜索外部表。如果用戶需要索引外部表,用戶就要選擇基于外部表的內容,建立標準表(或者索引組織表,如下所述)。任何可以對用戶的標準表進行索引。用戶可以使用Oracle的內部作業高度機制DBMS_JOB去管理外部表和標準表之間的引用刷新。
7.2.3?? 索引組織表
索引組織表(或者IOT)這種表結構可以存儲索引這樣的內容,以輔助查詢性能。索引組織表會以犧牲插入和更新性能為代價提供極好的查詢性能。假如要使用Webster辭典,搜索一些詞的意義,那么當用戶在辭典中搜索這個詞的時候,用戶就要將書打開到這個詞的附近位置。用戶可以根據詞的字母次序知道在哪里找到這個詞,然后再基于用戶打開書的位置進行前后搜索。這就是B樹的實際示例。這也是索引組織表的完美示例。
SQL> create table states(2 state_id varchar2(2),3 state_name varchar2(20),4 constraint states_pk5 primary key(state_id)6 )7 organization index8 / 表已創建。?
在這個救命中,我們擁有一個簡單的查找表,它由2個列構成:STATE_ID和STATE_NAME。這個表最常被訪問的方式是基于STATE_ID列查找STATE_NAME。語句末尾的ORGANIZATION INDEX會告訴Oracle這個表是索引組織表。
在索引組織表中,會根據表的主鍵列進行大B樹索引排序,將數據寫入磁盤。這樣能夠在使用表的主鍵列在IOT上進行查詢的時候得到更好的讀取性能。
IOT要比堆組織表更難于維護。當向堆表中寫入行的時候,Oracle會簡單地使用表盤區中的第一個可用空間寫入數據。然后對于IOT,當插入行的時候,它必須要將數據組織到它的“位置”。由于IOT要像索引一樣組織,所以Oracle要根據所插入新行的主鍵將數據寫入合適的數據塊。當前在數據塊中存儲的行會進行移動,以容納新寫入數據塊。在用戶應用中使用SQL訪問索引組織表與訪問堆組織表沒有區別。當用戶在堆組織表上提交查詢的時候,Oracle可以使用索引訪問表。這樣依賴于各種因素,例如(但是不限于):
- 所使用的優化器(基于開銷還是基于規則)
- 在查詢的WHERE子句中規定的列
- 是否相應于查詢的WHERE子句中的列,在表上建立了的索引
如果Oracle決定使用索引,就是因為所估計的開銷(執行查詢所需的資源)要小于執行全表搜索的開銷。如果Oracle決定使用索引,它首先會咨詢索引來判斷它需要讀取的數據塊,然后再執行那些讀取,獲取實際的數據。例如,我們來觀察Oracle怎樣基于表的行數執行索引,首先,我們要建立一個小表:
SQL> create table t as2 select * from all_objects where rownum<513 / 表已創建。?
所建立的這個表具有50行,所以我們要在OBJECT_ID列上建立一個索引:
現在,我們要分析這個表,幫助Oracle SQL優化器確定在我們查詢它的時候,是使用索引讀取還是全表搜索:
SQL> analyze table t compute statistics2 / 表已分析。?
我們現在可以使用SQL*Plus AUTOTRACE工具查看Oracle怎樣查詢數據:
(如果set autotrace on出現:
SP2-0613: 無法驗證 PLAN_TABLE 格式或實體 SP2-0611: 啟用EXPLAIN報告時出錯?
則參考“第6章 在Oracle中的處理語句”,開啟Oracle的跟蹤調試)
SQL> set autotrace onSQL> select * from t where object_id=10;未選定行Execution Plan ----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=89)1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=89)2 1 INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=1)Statistics ----------------------------------------------------------990 recursive calls0 db block gets175 consistent gets11 physical reads0 redo size915 bytes sent via SQL*Net to client372 bytes received via SQL*Net from client1 SQL*Net roundtrips to/from client22 sorts (memory)0 sorts (disk)0 rows processed SQL> set autotrace off?
當我基于這個索引中的列執行查詢時,用戶可能會認為Oracle會選擇執行索引讀取。然而,為了在這個表上執行索引讀取,我們至少要讀取一個索引塊和一個表數據塊。在這個例子中,表的完全執行將會需要更少的讀取。
我們現在來向表t增加更多的記錄:
SQL> drop table t2 / 表已丟棄。SQL> create table t as select * from all_objects where rownum<100012 / 表已創建。SQL> create index t_idx on t(object_id); 索引已創建。SQL> analyze table t compute statistics; 表已分析。SQL> set autotrace traceonly SQL> select * from t where object_id=102 / 未選定行Execution Plan ----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=83)1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=83)2 1 INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=1)Statistics ----------------------------------------------------------5 recursive calls0 db block gets6 consistent gets4 physical reads0 redo size915 bytes sent via SQL*Net to client372 bytes received via SQL*Net from client1 SQL*Net roundtrips to/from client2 sorts (memory)0 sorts (disk)0 rows processed?
現在,由于這里有了大量的行,所以用戶可以從AUTOTRACE命令的結果中看到,Oracle會選擇執行索引讀取,而不是全表搜索。
對于總是要通過特定索引訪問的表(具有不頻繁的更新或者插入),使用索引組織表來代替堆組織表可以提高性能。這是因為Oracle不必再決定是否要使用索引作為表的訪問路徑。另外,由于不需要同時存儲索引數據和表數據,Oracle的存儲要求也會最小。
索引組織表還有三個特性,我們現在來討論它們。
1?????????? COMPRESS和NOCOMPRESS
壓縮實際上是一個可以用于所有索引的選項,而不僅僅是IOT,它是指索引中的數據在數據塊中實際存儲的方式。COMPRESS屬性中規定的值(整數)直接對應于索引組織表的主鍵中不應該多次存儲的列數量。開始的列只要存儲一次,而隨后的表項只需存儲其余與最初表項不同的列。在以下的示例中,我們將要建立2個索引組織表,一個使用了壓縮,而另一個沒有,以展示這個內容:
SQL> connect hr/hr; 已連接。SQL> create table locations_iot(2 region_id,country_id,location_id,3 primary key(region_id,country_id,location_id)4 )5 organization index6 nocompress7 as select c.region_id,l.country_id,l.location_id8 from locations l,countries c9 where l.country_id=c.country_id10 / 表已創建。SQL> create table locations_iot_c(2 region_id,country_id,location_id,3 primary key(region_id,country_id,location_id)4 )5 organization index6 compress 27 as select c.region_id,l.country_id,l.location_id8 from locations l,countries c9 where l.country_id=c.country_id10 / 表已創建。?
這2個表都使用了幾乎相同的CREATE TABLE語句,利用了相同的數據來構建。唯一的區別就是2個語句的第7行的COMPRESS/NOCOMPRESS屬性。以下的表描述了數據在磁盤上的數據塊中的存儲方式。
表7-1 描繪了使用NOCOMPRESS設置作為壓縮屬性的LOCATIONS_IOT表。
表7-1 使用NOCOMPRESS設置的數據存儲
| 1,CH,2900 | 1,CH,3000 | 1,DE,2700 | 1,IT,1000 | 1,IT,1100 |
| 1,NL,3100 | 1,UK,2400 | 1,UK,2500 | 1,UK,2600 | 2,BR,2800 |
| 2,CA,1800 | 2,CA,1900 | 2,MX,3200 | 2,US,1400 | 2,US,1500 |
| 2,US,1600 | 2,US,1700 | 3,AU,2200 | 3,CN,2000 | 3,IN,2100 |
| 3,JP,1200 | 3,JP,1300 | 3,SG,2300 | ? | ? |
與此相似,在使用COMPRESS 2 設置作為壓縮屬性的LOCATIONS_IOT_C表中,見表7-2。
表7-2 使用COMPRESS 2設置的數據存儲
| 1,CH,2900 | 3000 | 1,DE,2700 | 1,IT,1000 | 1100 |
| 1,NL,3100 | 1,UK,2400 | 2500 | 2600 | 2,BR,2800 |
| 2,CA,1800 | 1900 | 2,MX,3200 | 2,US,1400 | 1500 |
| 1600 | 1700 | 3,AU,2200 | 3,CN,2000 | 3,IN,2100 |
| 3,JP,1200 | 3,JP,1300 | 3,SG,2300 | ? | ? |
在LOCATIONS_IOT表中,因為在表中沒有壓縮,所以每個數據塊表項都有所有的三個值。然而,在LOCATIONS_IOT_C表中,有的數據塊表項要比其余的小。例如,第一個數據塊表是1,CH,2900。而第二個表項是3000。這是因為第二個表項開始的2個列與第一個表項相同,不需要再進行重復。相同的壓縮也可以為如下REGION_ID/COUNTRY_ID組合實現:
- CH
- IT
- UK
- CA
- 2,US
這樣就可以使存儲在數據塊中的數據數量更少。
2?????????? OVERFLOW
通常,索引要表示表中的一個關鍵列(或者最多最少量的列)。一般情況下,不能夠在索引中存儲類似于大規模的VARCHAR2列或者LOB這樣的大型列。所以B樹索引將會采用小型的緊密聚焦的數據塊。然而,在索引組織表中,各個行的大小可能會非常大,將這樣的數據存儲成索引將會降低所獲得的性能。OVERFLOW是為這種類型的表提供的機制,它可以將經常要查詢的數據放在基本索引塊中,而將不經常查詢(或者較大的數據列)存儲在另外的段中,這種段稱為溢出段。有2個選項可以用來規定怎樣將數據存儲在,或者移植到溢出段中:INCLUDING和PCTTHRESHOLD。
3?????????? INCLUDING
當使用INCLUDING子句的時候,管理員可以規定表中的一個列,它會將列劃分為在表的常規數據段中存儲的列(包括主鍵列),和在溢出段中存儲的列(包括已列出的那個列)。
我們可以使用以上的LOCATIONS_IOT表,建立一個索引組織表,它會包含HR模式LOCATIONS表的列。關鍵列將會存儲在表的數據段中,而所有其它列將會存儲在溢出段中:
SQL> create table locations_inc(2 region_id,country_id,location_id,street_address,postal_code,city,state_province,3 primary key(region_id,country_id,location_id)4 )5 organization index6 nocompress7 overflow8 including street_address9 as select c.region_id,l.country_id,l.location_id,l.street_address,l.postal_code,l.city,l.state_ province10 from locations l,countries c11 where l.country_id=c.country_id12 / 表已創建。?
在這個例子中,作為索引組織表建立了名為LOCATIONS_INC的表。生成這個表的行是從LOCATIONS和COUNTRIES表中抽取的(如CREATE TABLE語句的AS SELECT子句所示)。LOCATION_INC表將會存儲在2個不同的段中,主段將會包含REGION_ID、COUNTRY_ID和LOCATION_ID列。第2個段,也就是溢出段,將會STREET_ADDRESS、POSTAL_CODE、CITY和STATE_PROVINCE列。
4?????????? PCTTHRESHOLD
一行中的所有數據都必須與由PCTTHRESHOLD值標識的數據塊百分比相匹配。任何大于這個尺寸的行都要分隔到2個存儲位置:
- 關鍵列存儲在表段中
- 所有其它列存儲在溢出段中
當用戶擁有變化長度的數據,沒有辦法判斷各行大小時,PCTTHRESHOLD就會很有效。通過將大量的數據存儲在溢出段中,訪問關鍵列的查詢性能就會提高,這是因為關鍵列會存儲在主表段中,這些查詢可以避免訪問更大的溢出段。
簡而言之,使用索引組織表的最好特性之一就是用戶不必改變用戶代碼就可以使用它們。如果只考慮SQL,那么索引組織表與其它的表完全相同,所以,如果用戶發現用戶應用中要經常讀取一個表,而且查詢大多數集中在主鍵列上,那么用戶就可以將這個表重新建立為索引組織表,進而獲得益處。然而,我們也注意到了它們的使用具有一些特點,只有通過試驗,并且在用戶應用中測試它們,用戶才能夠了解它們什么時候對用戶有用。
7.2.4?? 臨時表
Oracle的臨時表是那些只在事務處理或者會話進行期間存在數據的表。數據會在事務處理或者會話開始以后插入臨時表,當事務處理或者會話完成之后就會刪除。通過采用這種方式,開發者就可以在它們希望執行的應用邏輯(例如存儲PL/SQL代碼)的運行期間訪問臨時存儲區域。
注意:
臨時表完全不同于堆表。向堆表中插入數據非常缺乏效率,只有當事務處理結束時才可以將其刪除。
Oracle的臨時表與大多數其它關系數據庫供應商的臨時表的工作方式有所不同。用戶不需要在每次希望使用臨時存儲的時候都建立臨時表。與此相反,作為用戶應用開發的組成部分,就如同用戶建立常規應用表一樣,用戶只需建立一次臨時表,而且只需一次。通過使用這種方式,每當在用戶應用中使用臨時表存儲行的時候,就免除了重新建立表的負擔。因此,用戶可以向處理其它表一樣,使用相似的命名慣例,在開發過程中指定表的名稱。
相對于堆表,因為其它的用戶決不會使用這些記錄,所以臨時表不需要在它們包含的記錄上維護鎖定。與此同時,它們也不必維護過多的重做日志信息來防止數據庫故障,用戶不會希望從事務處理或者會話的中間繼續應用邏輯。
直到用戶實際向表中插入數據的時候,才會為臨時表分配空間,而與此相對的堆表會在CREATE TABLE 語句執行之后就分配一個區域。不僅如此,而且為存儲用戶數據分配的空間還來自于它們的臨時表空間,而不是與永久對象的數據存儲爭用表空間。
盡管用于臨時表的數據存儲機制與用于標準堆表的機制有明顯的差異,但是2種類型還具有一些相似性:
- 用戶能夠截取特定于用戶會話的臨時表(TRUNCATE TABLE命令將會在本章后面解釋)
- 用戶可以在臨時表上建立索引
- 用戶可以在臨時表上建立視圖
- 用戶可以在臨時表上建立觸發器(因為用戶不能夠在臨時表上建立外鍵,所以觸發器能夠用來輔助參考一致性)
應該在需要臨時存儲數據的應用中使用臨時表。以下的行為屬性列表展示了Oracle處理臨時表,提高應用性能的方式:
- 不為臨時表建立重做日志
- 直到在臨時表上使用了第一個INSERT語句之后才分配數據段
- TRUNCATE TABLE命令只會為發出這個命令的會話在表中截取數據。使用這個表的其它會話的數據不會受到影響
- 對于事務處理和會話范圍,對待臨時表上索引的方式與對待臨時表的方式相同
- 因為沒有2個會話或者事務處理能夠操作相同的行,所以在臨時表上不要求DML鎖定
試驗:建立臨時表
在這部分中,我們將要建立2個臨時表,一個針對事務處理,一個針對會話。這個試驗要展示2種臨時表類型之間的區別,讓用戶理解怎樣建立它們。
(1)????? 首先,我們要建立2個表(要注意:GLOBAL TEMPORARY TABLE中的GLOBAL是標準,沒有其它類型的臨時表)
SQL> create global temporary table session_tab2 on commit preserve rows3 as select * from employees4 / 表已創建。SQL> select count(*) from session_tab;COUNT(*) ----------107SQL> create global temporary table transaction_tab2 on commit delete rows3 as select * from employees4 where 1=05 / 表已創建。SQL> insert into transaction_tab2 select * from employees3 / 已創建107行。SQL> select count(*) from transaction_tab;COUNT(*) ----------107?
現在,我們來使用COMMIT命令,分析其影響
SQL> commit; 提交完成。SQL> select count(*) from session_tab;COUNT(*) ----------107SQL> select count(*) from transaction_tab;COUNT(*) ----------0?
就如用戶所見,經過COMMIT后,SESSION_TAB表中的記錄會得到保留,而TRANSACTION_TABL中的記錄會被刪除。
(2)????? 接下來,我們將要斷開會話,重新連接相同的用戶,查看重新啟動會話對表的影響。
SQL> disconnect; 從Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production With the Partitioning, OLAP and Oracle Data Mining options JServer Release 9.2.0.1.0 - Production中斷開SQL> connect hr/hr; 已連接。SQL> select count(*) from session_tab;COUNT(*) ----------0SQL> select count(*) from transaction_tab;COUNT(*) ----------0?
工作原理
ON COMMIT PRESERVE ROWS子句規定了對于各次提交,所有的行都應該原樣保留在表中。由于會話可以包括許多事務,而臨時表中的記錄可以跨事務處理保存在表中,所以PRESERVE ROWS就可以規定一個專用于會話的臨時表。
ON COMMIT DELETE ROWS規定了針對各次提交,臨時表中的所有行都會被刪除。由于提交會界定事務處理,所以DELETE ROWS可以規定專用于事務處理的臨時表。
用戶還可以注意到,當建立TRANSACTION_TAB表的時候,我們使用了子句:
WHERE clause of 1=0
這樣就可以避免在CREATE GLOBAL TEMPORARY TABLE語句執行之后,在CREATE語句中插入到表中的行都被刪除。因為作為規則,在DDL語句執行前后在后臺都會使用COMMIT語句(隱性提交)。在我們建立了TRANSACTION_TAB表之后,我們就可以使用INSERT INTO SELECT語句向表中插入數據。
以下是當前HR數據庫中會話的臨時表和事務的臨時表:
SQL> select table_name from user_tables; TABLE_NAME ------------------------------ COUNTRIES DEPARTMENTS EMPLOYEES JOBS JOB_HISTORY LOCATIONS LOCATIONS_INC LOCATIONS_IOT LOCATIONS_IOT_C REGIONS SESSION_TABTABLE_NAME ------------------------------ SYS_IOT_OVER_30463 TRANSACTION_TAB?
7.2.5?? 其它表類型
這里有三種我們需要順便提及的其它表類型,對它們進一步地討論將會超出書本的范圍。
- 可以將非常大的表分割成較小的片段(分區)建立分區表。對于應用,分區表實際上就像一個表,但是由于它們工作在獨立的分區上,而不是整個表,所以可能需要輔助管理員。
- 簇表,或者通常也稱為簇,是物理上存儲在一起的2個或者多個表。因為這些表被認為總是會一起受到查詢(使用SQL語句中的一些連接形式),所以會將表存儲在相同的數據塊中,而不是它們各自的數據塊中。由于所有需要的行都存儲在公共的數據塊上,所以這可以幫助減少查詢中所需的磁盤讀取量。
- 散列簇(Hash clustered)表與在磁盤上將2個或者多個表實際存儲在一起的簇表相似。兩者之間的區別是Oracle用來存儲和獲取行的方法。在簇表中,會使用分離索引中存儲的鍵值獲取行,而在散列上,Oracle會使用散列函數來判斷存儲所要獲取的行的數據塊的位置。
7.3????????? 表特性
當使用Oracle管理應用數據的時候,表的特性將會決定怎樣建立表,怎樣在磁盤上存儲表,以及當表生成和可以使用之后,應用最終執行方式。在這一節中,我們將要討論可以在CREATE TABLE和ALTER TABLE命令中使用,以規定應用中表行為的各種表屬性。
7.3.1?? TABLESPACE子句
在第5章中,我們學習到表空間是存儲數據庫對象的邏輯對象。當建立表的時候,必須將其放置在表空間中。這為存儲表數據提供了一個“桶”,它可以通過在CREATE TABLE和ALTER TABLE命令中使用TABLESPACE子句來實現。然而要注意,TABLESPACE子句是可選的,沒有明確規定TABLESPACE子句而建立的表會存放在建立表的用戶賬號的默認表空間中。我們要作為用戶SCOTT連接數據庫,并且使用USER_USERS視圖判定默認表空間名稱,來對其進行展示:
SQL> connect scott/tiger; 已連接。SQL> select default_tablespace from user_users; DEFAULT_TABLESPACE ------------------------------ SYSTEM?
用戶可以看到SCOTT模式的DEFAULT_TABLESPACE是SYSTEM。如果我們現在建立FOO,而不規定TABLESPACE,那么我們就可以查詢USER_TABLES視圖來確定表的TABLESPACE_NAME,如下所示:
SQL> create table foo(2 a int3 )4 / 表已創建。SQL> select table_name,tablespace_name2 from user_tables3 where table_name='FOO'4 / TABLE_NAME TABLESPACE_NAME ------------------------------ --------------- FOO SYSTEM?
這里的結果表明DEFAULT_TABLESPACE實際上是SYSTEM。為了規定表空間,我們需要刪除表FOO,然后使用如下代碼對其進行重新建立:
SQL> drop table foo; 表已丟棄。SQL> create table foo(2 a int)3 tablespace users4 / 表已創建。SQL> select table_name,tablespace_name2 from user_tables3 where table_name='FOO'4 / TABLE_NAME TABLESPACE_NAME ------------------------------ --------------- FOO USERS?
現在可以看到我們已經在USERS表空間中重新建立了表FOO。
要注意,如果沒有USERS表空間,用戶將會得到錯誤消息:
ORA-00959:tablespace ‘USERS’ does not exist.?
用戶可以使用如下ALTER USER語句對其進行修改(還要從管理賬號中):
alter user SCOTT quota unlimited on users;?
注意:
可以注意到,在Oracle中建立用戶的時候,可以規定用戶的默認表空間。這個特殊的子句是可選的,如果忽略,默認值就是SYSTEM。
這個子句通常會被忽略,用戶經常會在SYSTEM表空間中建立它們的所有對象。將用戶的所有對象放入SYSTEM表空間,就像將用戶的所有文件放入Windows上的C:\目錄中或Unix計算機上的根目錄(/)中一樣。這將導致一系列的問題,包括:
- 數據庫混亂。由于所有對象都在一個表空間中,所以數據庫的管理性會變差。
- 由于必須從SYSTEM表空間的相同驅動器中讀取應用的表數據,所以會導致性能問題(潛在的)。(表空間可以分布到多個磁盤/設備,但是這不是默認設置。)
- 空間爭用。SYSTEM表空間是Oracle存儲它自己操作實例所需數據的地方(例如數據詞典)。在這種情況下對空間的爭用將會破壞系統性能。
1?????????? 識別和消除SYSTEM入侵者
在為CREATE TABLE語句設置TABLESPACE子句之前,用戶應該檢查數據庫中的用戶賬號,以確保它們沒有將SYSTEM表空間設置為它們的默認表空間。在以下的查詢中,我們將會尋找我們的數據庫中所有違反了這個規則的模式。
SQL> connect scott/tiger; 已連接。SQL> select username,default_tablespace,temporary_tablespace2 from dba_users3 where default_tablespace='SYSTEM' or temporary_tablespace='SYSTEM'4 / USERNAME DEFAULT_TABLESPACE TEMPORARY_TABLESPACE ------------------------------ ------------------------------ SYS SYSTEM TEMP SYSTEM SYSTEM TEMP DBSNMP SYSTEM TEMP HR_AUDIT SYSTEM TEMP ORACLE_ADMIN SYSTEM TEMP SCOTT SYSTEM TEMP OUTLN SYSTEM TEMP WMSYS SYSTEM TEMP ORDSYS SYSTEM TEMP ORDPLUGINS SYSTEM TEMP MDSYS SYSTEM TEMP 已選擇11行。?
就如用戶所見,我們很多賬號將SYSTEM作為默認表空間。由于這個例子中我們只關心用戶SCOTT,所以將要修改SCOTT的默認表空間,以便可以將新表寫到SYSTEM表空間以外的地方:
SQL> alter user scott default tablespace users2 / 用戶已更改。SQL> select default_tablespace,temporary_tablespace2 from dba_users3 where username='SCOTT'4 / DEFAULT_TABLESPACE TEMPORARY_TABLESPACE ------------------------------ --------------------- USERS TEMP如果用戶SCOTT現在建立一個表,但是沒有明確規定TABLESAPCE子句,默認情況下,就會將表數據存儲在USERS表空間中:
SQL> connect scott/tiger; 已連接。SQL> create table t(2 a int3 )4 / 表已創建。SQL> select table_name,tablespace_name2 from user_tables3 where table_name='T'4 / TABLE_NAME TABLESPACE_NAME ------------------------------ --------------- T USERSSQL> select table_name,tablespace_name from user_tables; TABLE_NAME TABLESPACE_NAME ------------------------------ ------------------------- BONUS SYSTEM DEPT SYSTEM EMP SYSTEM FOO USERS SALGRADE SYSTEM T USERS 已選擇6行。?
注意:
不用為SYS或者SYSTEM模式修改默認表空間屬性或者臨時表空間屬性。它們是數據庫建立和管理的內部賬號。
7.3.2?? LOGGING和NOLOGGING
LOGGING和NOLOGGING是能夠隨CREATE TABLE語句使用的可選參數。術語日志記錄(logging)是指Oracle重做日志,它記錄了數據庫中數據的所有改變。如果所發生的故障使數據不能從內存傳遞到數據庫的管理文件,就可以從重做日志中獲取這些改變。這可以防止數據丟失,提高可靠性。
當在CREATE TABLE語句中規定NOLOGGING的時候,就認為這個表是非日志記錄表(nologging table)。在這個表上進行的操作可能會潛在導致數據庫中更少的日志記錄。
當建立非日志記錄表的時候,重做日志生成會受到抑止,但是這只針對于特定操作。在這些操作期間,所改變的數據細節將不予考慮。然而,這并不是說不會建立重做日志。由于數據庫的內部結構(特別是數據詞典)正在改變,所以所有這種改變都會被記錄。這會讓那么認為使用NOLOGGING建立他們的表時,就不會有重做日志活動的管理員感到迷惑。除了以下所列的活動外,表中所發生改變的重做日志活動如常。能夠利用NOLOGGING子句的活動如下所示:
- CREATE TABLE AS SELECT
- SQL*LOADER直接路徑載入
- 直接路徑插入(通過/*+APPEND */提示)
不要將LOGGING子句與NOLOGGING子句的目的混淆。常規的表操作都會記錄在重做日志中,以便在實例故障的時候進行恢復。就如我們討論的,唯一避免重做日志的方式就是使用全局臨時表。
?
7.3.3?? STORAGE子句
在數據庫中有效管理空間消耗將會直接影響數據庫增長和存儲數據的能力。當用戶在Oracle中建立對象的時候(例如表和索引),用戶就可以規定對象怎樣消耗磁盤上的空間。
當建立消耗存儲的對象時,它就會存儲在我們在第5章中介紹的稱為盤區的邏輯對象中,隨著對象的增長,它們會消耗起來越多的盤區。
在Oracle 8i以前,盤區和空間分配通常都要在數據詞典中管理。當Oracle要建立新盤區的時候,就要對數據詞典表進行低層查詢,尋找目錄表空間中的下一個可用盤區。當分配了盤區之后,還必須要使用新的盤區信息對數據詞典進行更新。目標表空間稱為詞典管理(dictionary-managed)表空間。如果同時要分配大量盤區,而Oracle一次只能夠在數據詞典中分配一個盤區,這種方式就會導致性能問題。
當規定CREATE TABLE命令的存儲子句的時候,用戶可以使用如下參數:
- INITIAL——這是所建立的第一個盤區的大小。
- NEXT——在表的第一個盤區堆滿之后,NEXT參數就會告訴Oracle為隨后的盤區分配的空間大小。
- PCTINCREASE——對于不能夠確定對象增長需求數據的管理員,PCTINCREASE參數可以提供“不斷增長”的下一個盤區的容量。每次分配盤區的時候,NEXT大小都要根據PCTINCREASE比例增長。然而,這意味著每次分配另一個盤區的時候,用戶的盤區容量都會增長,通常建議將PCTINCREASE設置為0.
- MINEXTENTS——當詞典管理空間中建立表的時候,管理員可以告訴Oracle在建立表的時候分配多個盤區。對于INITIAL和NEXT大小設置為1MB,PCTINCREASE設置為0,MINEXTENTS參數為5的表,意味著建立的時候分配5個1MB的盤區(5MB空間)。
- MAXEXTENTS——這個參數規定了可以為表分配的盤區數量的上限。對于INITIAL和NEXT大小設置為1MB,PCTINCREASE設置為0,MAXEXTENTS參數為10的表,意味著表不能夠超過10MB的大小(除非管理員改變表的MAXEXTENTS屬性)。
在Oracle 8i中,Oracle引入了局域管理(locally managed)表空間,使得INITIAL、NEXT、PCTINCREASE、MINEXTENTS和MAXEXTENTS存儲屬性都有些過時。這些類型的表空間為管理員提供了選擇,可以讓Oracle管理盤區。而不是手工配置和管理他們的表存儲。
1?????????? 存儲概要
局域管理表空間免除了數據庫管理員進行盤區分配和規模維護的負擔。在Oracle 9i中,用戶也有一些需要使用詞典管理表空間的理由。通常,使用統一盤區容易要比自動分配盤區更好一些,因為使用統一盤區容易可以讓數據庫中的自由盤區能夠為表空間中的其它段使用。
如果用戶需要利用用戶表空間的存儲屬性,就應該對其進行規定,而不是使用默認值。這樣就可以確保用戶能夠控制在表空間中存儲的對象類型,以及它們將要消耗的空間。不要在對象層次(表、索引等等)規定存儲屬性,而是要讓它們從表空間繼承它們的存儲子句。
7.3.4?? CACHE和NOCACHE
當在Oracle中執行全表搜索的時候,讀入緩存的數據塊將會存儲在最近最少使用列表(LRU)的最近最少使用的一端。這意味著只要執行“常規”查詢,必須向緩存中讀取數據的時候,就會將這些數據塊換出緩存。
當建立表的時候,CACHE子句可以忽視這種行為。當使用CACHE子句建立的表上執行全表搜索的時候,會將數據塊讀入緩存,并且放置到LUR的最近最常使用的一段。
應該為小搜索表,以及因為這樣或者那樣的原因沒有使用索引的表規定CACHE。沒有索引的讀取經常會導致全表搜索,由全表搜索讀取的數據塊會很快被換出SGA。如果要相當頻繁地訪問用戶表,那么CACHE子句就可以幫助最小化數據塊的物理讀取。
NOCACHE是建立表的時候的默認值。沒有明確規定CACHE子句時所建立的表就是NOCAHE表。從這樣的表中讀取的數據塊通常會被交換出SGA。
7.4????????? 修改表
在Oracle數據庫中使用表并不困難,但是通過一次嘗試就建立一個完美的表幾乎不可能的。在建立了表,并且開始使用之后,很有可能會遇到沒有預計的需求或者問題。所以,Oracle擁有了非常詳細的ALTER TABLE語句來改變所有方面的表屬性。
表可以進行許多類型的修改,詳細地討論這些問題已經超出了本章的范圍。與此相反,這部分將會涵蓋一些最常使用的ALTER TABLE命令,以及怎樣有效地使用它們。它們是:
- 在表中增加、修改或者刪除列
- 重命名表
- 將表移動到新的表空間
- 改變表的存儲屬性
- 改變表的特性,例如LOGGING和NOLOGGING或者CACHE和NOCACHE
7.4.1?? 改變表中的列
在項目的開發和維護階段,需要對表進行修改,以適應新的數據需求和各種功能改變。從性能上講,刪除表,并且對其進行重新建立是不可行的,所以Oracle提供了增加新列、修改已有列,以及從已有表中刪除列而不影響表中其它數據的能力。
在以下示例中,我們將要建立一個表,并且使用ALTER TABLE命令去修改表列。我們首先會建立表PEOPLE,并且插入一些值:
SQL> connect hr/hr 已連接。SQL> create table people(2 employee_id number(9),3 first_name varchar2(15),4 last_name varchar2(20),5 email varchar2(25),6 constraint pk_people primary key(employee_id)7 )8 / 表已創建。SQL> insert into people2 values(1,'Tom','Kyte','tkyte@us.oracle.com'); 已創建 1 行。SQL> insert into people2 values(2,'Sean','Dillon','sdillon@us.oracle.com'); 已創建 1 行。SQL> insert into people2 values(3,'Christopher','Beck','clbeck@us.oracle.com'); 已創建 1 行。SQL> commit; 提交完成。?
為了驗證記錄插入成功,我們要查詢PEOPLE表,如下所示:
SQL> select * from people; EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL ----------- --------------- -------------------- ---------------------1 Tom Kyte tkyte@us.oracle.com2 Sean Dillon sdillon@us.oracle.com3 Christopher Beck clbeck@us.oracle.com?
就如用戶所見,我們的表現在已經填充了數據。我們要做的第一件工作就是使用如下語句增加一個PHONE_NUMBER列:
SQL> alter table people2 add(3 phone_number varchar2(10)4 )5 / 表已更改。SQL> select * from people; EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMB ----------- --------------- -------------------- ------------------------- ----1 Tom Kyte tkyte@us.oracle.com2 Sean Dillon sdillon@us.oracle.com3 Christopher Beck clbeck@us.oracle.com?
就如用戶所見,由于我們已經向表中增加了新的列,但是沒有修改已有的行,所以,數據庫中沒有行會包含新列數據。
7.4.2?? NOT NULL列約束
只有當表中沒有記錄的時候,用戶才能夠規定說明NOT NULL作為約束的列。這是因為當在列上應用NOT NULL約束的時候,Oracle就會嘗試驗證表中的所有行。如果已經存在記錄,那么這些記錄就不能夠通過這些驗證,因此也就不能夠增加約束,進而也不能夠增加列。然而,有一個解決這個問題的變通方式。我們可以首先向表中增加所需的列(在這個例子中是SSN)。
SQL> alter table people2 add(3 ssn number(9)4 )5 / 表已更改。SQL> update people2 set ssn=2345678903 where employee_id=1; 已更新 1 行。SQL> update people2 set ssn=3456789013 where employee_id=2; 已更新 1 行。SQL> update people2 set ssn=4567890123 where employee_id=3; 已更新 1 行。SQL> alter table people2 modify(3 ssn number(9) not null4 )5 / 表已更改。SQL> desc people; 名稱 是否為空? 類型 ----------------------------------------------------------------------------EMPLOYEE_ID NOT NULL NUMBER(9)FIRST_NAME VARCHAR2(15)LAST_NAME VARCHAR2(20)EMAIL VARCHAR2(25)PHONE_NUMBER VARCHAR2(10)SSN NOT NULL NUMBER(9)?
就如用戶所見,NOT NULL約束現在已經成功應用于SSN列。
這里要簡單提及的是,如果用戶要通過約束數據類型來修改用戶表列例如紛亂數據類型的長度,那么如果列中已有的數據要違反遵循改變的列數據類型時,Oracle就會拒絕改變。
7.4.3?? 刪除列以及標注不用列
用戶不僅可以修改已經存在于表中的列,而且還可以完全刪除它們。在Oracle 8i以前,表中所有沒有使用的列都作為附加負荷承擔,或者使用CREATE TABLE AS SELECT語句重新建立沒有不想要的列的新表。然而,在Oracle 8i及其以上版本中,就可以簡單使用如下語句對列進行刪除:
ALTER TABLE <table name> DROP COLUMN?
這個操作會將表重新寫入到磁盤,并且移走舊有的列數據,這是一種“回收”曾經由不想再使用的列所使用的空間的方式。
ALTER TABLE <table name> SET UNUSED COLUMN <column name>?
這個命令與ALTER TABLE <table name> DROP COLUMN命令有所區別,它不會對表進行重寫,也不會收回空間。在這個語句執行之后,列只會被簡單忽略。因為在列被完全刪除之前,不能夠對數據進行覆寫,所以這會導致區域的數據存儲的丟失。如果必須要回收這些丟失的存儲空間,就要對表進行重新組織(列會被刪除)。
在ALTER TABLE的DROP COLUMN和SET UNUSED COLUMN變種中,用戶可以一次刪除或者標注多個列。為了展示這些內容,我們要作為用戶SCOTT注冊,并且使用HR模式的表,我們首先要為SCOTT賦予HR所擁有的表上的必要特權:
SQL> connect hr/hr 已連接。SQL> grant select on departments to scott; 授權成功。SQL> grant select on locations to scott; 授權成功。SQL> grant select on countries to scott; 授權成功。SQL> grant select on regions to scott; 授權成功。?
現在,我們可以建立DEPARTMENTS表,然后執行SELECT語句,驗證在我們的表中已經包含了所需要的信息:
SQL> connect scott/tiger 已連接。SQL> create table departments as2 select d.department_id,d.department_name,d.manager_id,d.location_id,c.country_name,r.region_name3 from hr.departments d,hr.locations l,hr.countries c,hr.regions r4 where d.location_id=l.location_id5 and l.country_id=c.country_id6 and c.region_id=r.region_id7 / 表已創建。SQL> select department_name,country_name,region_name2 from departments3 order by 3,2,14 / DEPARTMENT_NAME COUNTRY_NAME REGION_NAME ------------------------------ ---------------------------------------- -- Marketing Canada Americas Accounting United States of America Americas Administration United States of America Americas . . . Public Relations Germany Europe Human Resources United Kingdom Europe Sales United Kingdom Europe 已選擇27行。?
然而,在建立了表之后,我們現在意識到不需要在DEPARTMENTS表中維護COUNTRY_NAME或REGION_NAME列,這些值可以在其它表中得到。出現這種考慮,我們需要刪除這些列。我們可以一次刪除一列:
SQL> alter table departments2 drop column country_name3 / 表已更改。SQL> alter table departments2 drop column region_name3 / 表已更改。或者實際上,我們也能夠在同一個語句中同時刪除2列:
SQL> --我們可以增加回舊字段! SQL> alter table departments2 add(3 country_name varchar2(40),4 region_name varchar2(15)5 )6 / 表已更改。SQL> alter table departments2 drop(3 country_name,region_name)4 / 表已更改。?
在使用的情況下,用戶可能無法重寫它們(特別它們一直在使用的時候)。用戶可以不刪除它們,而是將它們設置為UNUSED,隨后,用戶可以將它們一起刪除。在DEPARTMENTS的例子中,我們可以使用如下方式對其進行實現:
SQL> alter table departments2 add(3 country_name varchar2(40),4 region_name varchar2(15)5 )6 / 表已更改。SQL> alter table departments2 set unused(3 country_name,region_name)4 / 表已更改。?
為了確定在我們的表中有多少沒有使用的列,我們可以USER_UNUSED_COL_TABS數據詞典視圖,如下所示:
SQL> select * from user_unused_col_tabs; TABLE_NAME COUNT ------------------------------ ---------- DEPARTMENTS 2?
這個表不需要全天在線,或者當用戶計劃維護的時候,用戶就可以使用我們在前面看到的命令,從數據庫中將這些列刪除,回收存儲空間:
ALTER TABLE departments DROP UNUSED COLUMNS?
要注意,如果用戶使用了編碼向DEPARTMENTS表中插入記錄的時候:
insert into departments(..) values(..)?
如果COUNTRY_NAME列或者REGION_NAME列已經刪除(或者標記為不可用)的時候,這些代碼就會中斷。
7.4.4?? 重命名表
改變表的名稱是一個相對容易執行的任務。ALTER TABLE命令如下所示:
SQL> connect hr/hr 已連接。SQL> alter table people2 rename to employees3 /SQL> alter table people2 rename to people013 / 表已更改。?
注意:
對表進行重命名非常容易,但是影響卻非常大。在需要對表的名稱進行修改的時候,要格外小心。盡管Oracle可以自動更新數據詞典中的外鍵、約束定義以及表關系,但是它還不能夠更新數據庫中的存儲代碼模塊、存儲報告或者查詢,或者客戶應用。對于后來這些使用表的對象(例如客戶應用),當表重命名之后,就會失敗。
7.4.5?? 將表移動到新表空間或者存儲
存儲表的方式以及讀取表的磁盤都會影響數據庫的整體性能。通常,整個數據庫都要使用預先設計的附屬表存儲體系結構構建。可以根據數據庫訪問表的方式、其它數據庫的對象存放地點以及存儲介質的物理結構來確定表的所屬空間。表的存儲屬性要從所建立表的類型,以及怎樣在應用中使用它的角度進行指定。
這里是管理員希望改變表的存儲屬性的一些原因,包括:
- 表處于不適合的表空間中
- 應用改變使用表的方式
- 存儲在表中的數據發生改變(換句話說,加入了新列或者改變了已有的列)
?
語句可以實現這個目的。為了展示這一點,我們來瀏覽我們的數據庫,查看是否有表在SYSTEM表空間中:
SQL> connect scott/tiger; 已連接。SQL> select tablespace_name,table_name2 from user_tables3 where table_name in ('EMP','DEPT','BONUS','SALGRADE')4 order by 1,25 / TABLESPACE_NAME TABLE_NAME ------------------------------ --------------------------- SYSTEM BONUS SYSTEM DEPT SYSTEM EMP SYSTEM SALGRADE?就如我們早先解釋的,將表存儲在這個表空間中通常是不好的習慣,我們想要將這些表移動到另一個表空間中。為了看到數據實際從一個段移動到另一個段,我們將會在我們執行ALTER TABLE語句的前后,查看數據詞典視圖USER_SEGMENTS:
SQL> select segment_name,tablespace_name2 from user_segments3 where segment_name='EMP'4 /SEGMENT_NAME TABLESPACE_NAME ---------------------------------------------------------------------------- EMP SYSTEMSQL> alter table emp move2 tablespace users3 / 表已更改。SQL> select segment_name,tablespace_name2 from user_segments3 where segment_name='EMP'4 / SEGMENT_NAME TABLESPACE_NAME ---------------------------------------------------------------------------- EMP USERS?
當我們將表從SYSTEM表空間移動到USERS表空間的時候,也會將段從一個表空間移植到另一個表空間。由于段實際上是在數據文件中存儲的數據塊,而USERS表空間與SYSTEM表空間具有不同的數據文件,所以也會將數據在物理上移動到另一個數據文件。
7.4.6?? 改變不同的表特性
有的時候,當建立表的時候,不可能知道在它們所支持的應用的生命周期期間,施加給這些表的所有要求。我們試圖盡可能具有前瞻性地構建表,但是我們會不時地意識到通過改變的屬性可以獲得更好的性能,或者消耗更少的資源。
例如,如果應用擁有會頻繁進行完全搜索的表,而且這是必須的行為,那么將它的數據志緩存在緩存內存區域中就會獲益。如果由于執行其它查詢,要將數據塊交換出內存,那么這個表的數據就要被讀入內存、交換出內存、再次讀入內存。與此相反,我們在這個表上設置CACHE屬性會通知Oracle將數據塊放到“最近最少使用”列表的“最近最常使用”一端,進而強制Oracle將數據塊保留在內存中(至少可以保留更長一段時間)。這可以使用如下ALTER TABLE命令實現:
alter table <table name> [cache|nocache];?
相同的方法也可以用于表的LOGGING和NOLOGGING特性:
alter table <table name> [logging|nologging];?
7.4.7?? ALTER TABLE總結
本章已經涵蓋了一些用戶最常對表進行的改變類型。它確實沒有包含Oracle可以讓它的用戶進行改變的所有。表的大多數屬性都可以進行修改。而且,對于我們在本章前面討論的不同類型的表,還可以改變那些屬于這些表類型的屬性。ALTER TABLE命令的完整文檔可以在Oracle SQL Reference中找到。
7.5????????? 刪除表
DROP TABLE命令的語法如下所示:
DROP TABLE <TABLE_NAME>[ CASCADE CONSTRAINTS ];?
在以下救命中,我們要建立和生成表DROP_ME,然后刪除它:
SQL> create table drop_me(2 a int,3 b int4 )5 / 表已創建。SQL> insert into drop_me values(1,1); 已創建 1 行。SQL> insert into drop_me values(1,2); 已創建 1 行。SQL> insert into drop_me values(2,1); 已創建 1 行。SQL> drop table drop_me; 表已丟棄。?
刪除表中的所有數據,和從數據庫中刪除表具有根本的不同。即使用戶使用DELETE命令從表中刪除了所有記錄,表仍然會存在,在所有記錄被刪除之后還可以使用。然后,當用戶刪除表的時候,表就不復存在,向表中插入記錄的唯一方法就是使用相同的表特性建立一個新表。
CASCADE CONSTRAINTS
DROP TABLE 命令有一個唯一的可選參數,稱為CASCADE CONSTRAINTS。這個參數可以用于那些所擁有的外鍵引用所刪除表的表。如果沒有規定CASCADE CONSTRAINTS,那么當管理員試圖刪除在子表中具有記錄的表時,操作就會失敗,并且會向用戶發出一個錯誤。通過規定CASCADE CONSTRAINTS,將會刪除所有子表外鍵。
7.6????????? TRUNCATE TABLE
TRUNCATE TABLE是用來刪除所有數據,但是不刪除表本身的DDL語句。為這個表提供的索引也可以被截去。TRUNCATE TABLE能夠用于堆組織表,索引組織表,以及臨時表。使用如下語法就可以執行TRUNCATE TABLE命令:
TRUNCATE TABLE [ SCHEMA.] <table name> [ DROP STORAGE | REUSE STORAGE]?
TRUNCATE TABLE是從表中刪除所有記錄的快速方法,而且因為它不生成回滾數據,所以它也比使用DELETE命令更有效。
當使用TRUNCATE TABLE的時候,有一些需要注意的要點:
- 為了執行TRUNCATE TABLE命令,用戶必須具有DROP TABLE特權。
- 在使用TRUNCATE TABLE之前,必須禁用所有子表外鍵。如果有任何表引用了正在被刪除的表,那么語句就會失敗。(子引用外鍵不必禁用)
- 在TRUNCATE TABLE操作期間,不會激活ON DELETE觸發器。
- 由于TRUNCATE TABLE是一個DDL語句,所以在它執行前后會進行提交,不能夠被回滾。
7.6.1?? DROP STORAGE或者REUSE STORAGE
在表的存續期間,會分配盤區來存儲生成表的數據行。表可能只分配了由表的MINEXTENTS存儲屬性所指定的盤區數量,或者它也可以根據插入表中的行的數量、各行的大小以及盤區的容量,分配了成百上千的盤區。當使用TRUNCATE TABLE語句的時候,管理員必須要決定是要將這些盤區返還給表空間,以便由其它的對象所使用,還是保留這些盤區讓這個表來使用。
1?????????? DROP STORAGE
DROP STORAGE是TRUNCATE TABLE的默認行為。當使用DROP STORAGE的時候,就會恢復表的最初存儲特性。這意味著只有最初為表分配的盤區(MINEXTENTS)會被保留以用于表中的新行,所有附加的盤區都會被釋放,以便表空間中的其它對象使用。如果表不會返回到它的最初大小,或者要花很長的時候才會增長回它最初的大小,就應該使用DROP STORAGE。這可以確保在表空間中長時間不會有大塊的存儲空間,或者在更糟的情況下,永遠不能夠被其它對象所使用。
REUSE STORAGE
如果使用了REUSE STORAGE子句,那么所有為表存儲分配的盤區都會保留,表將會使用它們向表中插入新行。對于快速增長的表,為了要為插入的新行清空表,就可以使用TRUNCATE語句快速刪除行,并且應該使用REUSE STORAGE。這可以讓Oracle不必為這個表不斷分配新的盤區。
7.6.2?? 截取臨時表
當截取臨時表的時候,只會刪除用戶在會話期間插入表的行。當進程需要在會話結束之前,清空并且重新生成表的時候,就可以使用這種非常好的方式來重置特定于會話的臨時表。在專用于事務處理的臨時表上使用TRUNCATE TABLE不甚明智,因為COMMIT將會更有效率在特定于事務處理的臨時表中,所以COMMIT命令將會刪除表中的行,但是空間不能夠重用。
7.7????????? 小結
在討論索引組織表、外部表、以及臨時表類型的時候,我們已經展示了怎樣根據用戶的特定需求建立表特性,進而輔助提高性能。使用這些表類型時所獲得的經驗將會極大地加強用戶數據庫的效率。
?
文章根據自己理解濃縮,僅供參考。
摘自:《Oracle編程入門經典》 清華大學出版社?http://www.tup.com.cn
from:?http://www.cnblogs.com/yongfeng/archive/2013/02/24/2924649.html
總結
以上是生活随笔為你收集整理的Oracle编程入门经典 第7章 表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle编程入门经典 第6章 在Or
- 下一篇: Oracle编程入门经典 第8章 索引