外部表概述
外部表只能在Oracle 9i之后來使用。簡單地說,外部表,是指不存在於數據庫中的表。通過向Oracle提供描述外部表的元數據,我們可以把一個操作系統文件當成一個只讀的數據庫表,就像這些數據存儲在一個普通數據庫表中一樣來進行訪問。外部表是對數據庫表的延伸。
外部表的特性
位於文件系統之中,按一定格式分割,如文本文件或者其他類型的表可以作為外部表。
對外部表的訪問可以通過SQL語句來完成,而不需要先將外部表中的數據裝載進數據庫中。
外部數據表都是只讀的,因此在外部表不能夠執行DML操作,也不能創建索引。
ANALYZE語句不支持采集外部表的統計數據,應該使用DMBS_STATS包來采集外部表的統計數據。
創建外部表的注意事項
1.需要先建立目錄對象
在建立對象的時候,需要小心,Oracle數據庫系統不會去確認這個目錄是否真的存在。如果在輸入這個目錄對象的時候,不小心把路徑寫錯了,那可能這個外 部表仍然可以正常建立,但是卻無法查詢到數據。由於建立目錄對象時,缺乏這種自我檢查的機制,為此在將路徑賦予給這個目錄對象時,需要特別的注意。另外需 要注意的是路徑的大小寫。在Windows操作系統中,其路徑是不區分大小寫的。而在Linux操作系統,這個路徑需要區分大小寫。故在不同的操作系統 中,建立目錄對象時需要注意這個大小寫的差異
2.對於操作系統文件的要求
建立外部表時,必須指定操作系統文件所使用的分隔符號。並且該分隔符有且只有一個。創建外部表時,不能含有標題列。如果這個標題信息與外部表的字段類型不一致(如字段內容是number數據類型,而標題信息則是字符型數據,則在查詢時就會出錯)。如果數據類型恰巧一致的話,這個標題信息Oracle數據庫也會當作普通記錄來對待。
當Oracle數據庫系統訪問這個操作系統文件的時候,會在這個文件所在的目錄自動創建一個日志文件。無論最后是否訪問成功,這個日志文件都會如期建立。查看這個日志文件,可以了解數據庫訪問外部表的頻率、是否成功訪問等等。默認情況下,該日志在與外部表的相同directory下產生。
3.在建立臨時表時的相關限制
對表中字段的名稱存在特殊字符的情況下,必須使用英文狀態的下的雙引號將該表列名稱連接起來。如采用”SalseID#”。
對於列名字中特殊符號未采用雙引號括起來時,會導致無法正常查詢數據。
建議不用使用特殊的列標題字符
在創建外部表的時候,並沒有在數據庫中創建表,也不會為外部表分配任何的存儲空間。
創建外部表只是在數據字典中創建了外部表的元數據,以便對應訪問外部表中的數據,而不在數據庫中存儲外部表的數據。
簡單地說,數據庫存儲的只是與外部文件的一種對應關系,如字段與字段的對應關系。而沒有存儲實際的數據。
由於存儲實際數據,故無法為外部表創建索引,同時在數據使用DML時也不支持對外部表的插入、更新、刪除等操作。
4.刪除外部表或者目錄對象
一般情況下,先刪除外部表,然后再刪除目錄對象,如果目錄對象中有多個表,應刪除所有表之后再刪除目錄對象。
如果在未刪除外部表的情況下,強制刪除了目錄,在查詢到被刪除的外部表時,將收到"對象不存在"的錯誤信息。
查詢dba_external_locations來獲得當前所有的目錄對象以及相關的外部表,同時會給出這些外部表所對應的操作系統文件的名字。 如果只是在數據庫層面上刪除外部表,並不會自動刪除操作系統上的外部表文件。
5.對於操作系統平台的限制
不同的操作系統對於外部表有不同的解釋和顯示方式
如在Linux操作系統中創建的文件是分號分隔且每行一條記錄,但該文件在Windows操作系統上打開則並非如此。
建議避免不同操作系統以及不同字符集所帶來的影響
創建外部表
使用CREATE TABLE語句的ORGANIZATION EXTENERAL子句來創建外部表。外部表不分配任何盤區,因為僅僅是在數據字典中創建元數據。
1.外部表的創建語法
createtabletable_name
(col1 datatype1,col2 datatype2,col3 datatype3)
organization exteneral
(.....)
詳細語法可參見筆者的另兩篇文章
Oracle外部表ORACLE_DATAPUMP類型的創建語法詳解:http://czmmiao.iteye.com/blog/1268453
Oracle外部表ORACLE_LOADER類型的創建語法詳解:http://czmmiao.iteye.com/blog/1268157
2.由查詢結果集,使用Oracle_datapump來填充數據來生成外部表
a.創建系統目錄以及Oracle數據目錄名來建立對應關系,同時授予權限
$ mkdir -p /home/oracle/external_tb/data
create or replace directory data_dir as '/home/oracle/external_tb/data/'; grant read,write on directory data_dir to scott;
b.創建外部表
create table ex_tb1 (ename,job,sal,dname) organization external (type oracle_datapump default directory data_dir location('ex_tb1')) parallel 1 as select ename,job,sal,dname from emp join dept on emp.deptno=dept.deptno;
c.驗證外部表
select * from ex_tb1; ENAME JOB SAL DNAME ------------------------- -------------------- ---- ------------------------- CLARK MANAGER 2450 ACCOUNTING KING PRESIDENT 5000 ACCOUNTING MILLER CLERK 1300 ACCOUNTING JONES MANAGER 2975 RESEARCH FORD ANALYST 3000 RESEARCH ADAMS CLERK 1100 RESEARCH SMITH CLERK 800 RESEARCH SCOTT ANALYST 3000 RESEARCH WARD SALESMAN 1250 SALES TURNER SALESMAN 1500 SALES ALLEN SALESMAN 1600 SALES JAMES CLERK 950 SALES BLAKE MANAGER 2850 SALES MARTIN SALESMAN 1250 SALES 14 rows selected.
對於使用上述方式創建的外部表可以將其復制到其他路徑作為外部表的原始數據來生成新的外部表,用於轉移數據。
d.將外部表文件復制一個新的文件名,用以模擬到其他服務器上
$ cp /home/oracle/external_tb/data/ex_tb1 /home/oracle/external_tb/data/in_tb1
e. 新建表,將上述外部表的數據導入到新表中
create table in_tb1 (ename varchar2(10),job varchar2(9),sal number(7,2),dname varchar(14)) organization external (type oracle_datapump default directory data_dir location('in_tb1'));
f.驗證新外部表的數據
select * from in_tb1; ENAME JOB SAL DNAME ------------------------- -------------------- ---- ------------------------- CLARK MANAGER 2450 ACCOUNTING KING PRESIDENT 5000 ACCOUNTING MILLER CLERK 1300 ACCOUNTING JONES MANAGER 2975 RESEARCH FORD ANALYST 3000 RESEARCH ADAMS CLERK 1100 RESEARCH SMITH CLERK 800 RESEARCH SCOTT ANALYST 3000 RESEARCH WARD SALESMAN 1250 SALES TURNER SALESMAN 1500 SALES ALLEN SALESMAN 1600 SALES JAMES CLERK 950 SALES BLAKE MANAGER 2850 SALES MARTIN SALESMAN 1250 SALES 14 rows selected.
g.創建正常的表,將外部表數據導入,這就是利用ORACLE_DATAPUMP類型的額外部表實現數據遷移
create table tb1 as select * from in_tb1;
3.使用外部文件數據,使用oracle_loader來填充數據來生成外部表
a.准備外部數據源文件
cat /home/oracle/external_tb/data/1.txt "7369","SMITH","CLERK","7902","17-DEC-80","100","0","20" "7499","ALLEN","SALESMAN","7698","20-FEB-81","250","0","30" "7521","WARD","SALESMAN","7698","22-FEB-81","450","0","30" "7566","JONES","MANAGER","7839","02-APR-81","1150","0","20" $ cat /home/oracle/external_tb/data/2.txt "7654","MARTIN","SALESMAN","7698","28-SEP-81","1250","0","30" "7698","BLAKE","MANAGER","7839","01-MAY-81","1550","0","30" "7934","MILLER","CLERK","7782","23-JAN-82","3500","0","10"
b.創建外部表
create table emp_new( emp_id number(4), ename varchar2(15), job varchar2(12), mgr_id number(4), hiredate date, salary number(8), comm number(8), dept_id number(2) ) organization external ( type oracle_loader default directory data_dir access parameters( records delimited by newline badfile 'emp_new%a_%p.bad' logfile 'emp_new%a_%p.log' fields terminated by ',' optionally enclosed by '"' lrtrim missing field values are null reject rows with all null fields ) location ('1.txt','2.txt') ) parallel reject limit unlimited;
c.驗證外部表
select * from emp_new; EMP_ID ENAME JOB MGR_ID HIREDATE SALARY COMM DEPT_ID ------ ---------- --------------- ---------- ------------------- ---------- ---------- ---------- 7654 MARTIN SALESMAN 7698 1981-09-28 00:00:00 1250 0 30 7698 BLAKE MANAGER 7839 1981-05-01 00:00:00 1550 0 30 7934 MILLER CLERK 7782 1982-01-23 00:00:00 3500 0 10 7369 SMITH CLERK 7902 1980-12-17 00:00:00 100 0 20 7499 ALLEN SALESMAN 7698 1981-02-20 00:00:00 250 0 30 7521 WARD SALESMAN 7698 1981-02-22 00:00:00 450 0 30 7566 JONES MANAGER 7839 1981-04-02 00:00:00 1150 0 20 7 rows selected.
4.外部表相關視圖
a.查看外部表信息
select TABLE_NAME,TYPE_NAME,DEFAULT_DIRECTORY_NAME,REJECT_LIMIT,ACCESS_PARAMETERS from user_external_tables;
b.獲得平面文件的位置
select * from user_external_locations order by table_name; TABLE_NAME LOCATION DIRECTORY DIRECTORY_NAME ---------- ---------- --------- -------------------- EMP_NEW 1.txt SYS DATA_DIR EMP_NEW 2.txt SYS DATA_DIR EX_TB1 ex_tb1 SYS DATA_DIR IN_TB1 in_tb1 SYS DATA_DIR
外部表定義的幾個重點
1.ORGANIZATION EXTERNAL關鍵字,必須要有。以表明定義的表為外部表。
2..重要參數外部表的類型
ORACLE_LOADER:定義外部表的缺省方式,只能只讀方式實現文本數據的裝載。
ORACLE_DATAPUMP:支持對數據的裝載與卸載,數據文件必須為二進制dump文件。可以從外部表提取數據裝載到內部表,也可以從內部表卸載數據作為二進制文件填充到外部表。
3.DEFAULT DIRECTORY:缺省的目錄指明了外部文件所在的路徑
4.LOCATION:定義了外部表的位置
5.ACCESS PARAMETERS:描述如何對外部表進行訪問
RECORDS關鍵字后定義如何識別數據行
DELIMITED BY 'XXX'——換行符,常用newline定義換行,並指明字符集。對於特殊的字符則需要單獨定義,如特殊符號,可以使用OX'十六位值',例如tab(/t)的十六位是9,則DELIMITEDBY0X'09';
cr(/r)的十六位是d,那么就是DELIMITEDBY0X'0D'。
SKIP X ——跳過X行數據,有些文件中第一行是列名,需要跳過第一行,則使用SKIP 1。
FIELDS關鍵字后定義如何識別字段,常用的如下:
FIELDS:TERMINATED BY 'x'——字段分割符。
ENCLOSED BY 'x'——字段引用符,包含在此符號內的數據都當成一個字段。
例如一行數據格式如:"abc","a""b,""c,"。使用參數TERMINATED BY ',' ENCLOSED BY '"'后,系統會讀到兩個字段,第一個字段的值是abc,第二個字段值是a"b,"c,。
LRTRIM ——刪除首尾空白字符。
MISSING FIELD VALUES ARE NULL——某些字段空缺值都設為NULL。
對於字段長度和分割符不確定且准備用作外部表文件,可以使用UltraEdit、Editplus等來進行分析測試,如果文件較大,則需要考慮將文件分割成小文件並從中提取數據進行測試。
外部表對錯誤的處理
REJECT LIMIT UNLIMITED
在創建外部表時最后加入LIMIT子句,表示可以允許錯誤的發生個數。默認值為零。設定為UNLIMITED則錯誤不受限制
BADFILE和NOBADFILE子句
用於指定將捕獲到的轉換錯誤存放到哪個文件。如果指定了NOBADFILE則表示忽略轉換期間的錯誤
如果未指定該參數,則系統自動在源目錄下生成與外部表同名的.BAD文件BADFILE記錄本次操作的結果,下次將會被覆蓋 LOGFILE和NOLOGFILE子句
同樣在access parameters中加入LOGFILE 'LOG_FILE.log'子句,則所有Oracle的錯誤信息放入'LOG_FILE.log'中
而NOLOGFILE子句則表示不記錄錯誤信息到log中,如忽略該子句,系統自動在源目錄下生成與外部表同名的.LOG文件
注意以下幾個常見的問題
1.外部表經常遇到BUFFER不足的情況,因此盡可能的增大READSIZE
2.換行符不對產生的問題。在不同的操作系統中換行符的表示方法不一樣,碰到錯誤日志提示如是換行符問題,可以使用
UltraEdit打開,直接看十六進制
3.特定行報錯時,查看帶有"BAD"的日志文件,其中保存了出錯的數據,用記事本打開看看那里出錯,是否存在於外部表定義相沖突
外部表的局限性
1.SQLLDR可以指定多少提交一次,即ROWS=?, 外部表卻沒有,這對於大數據量的導入有些不方例。
2.sqlldr errors表示允許錯誤的行數,外部表用REJECT LIMIT UNLIMITED,這個功能上基本相同。
3.外部表的列不能指定為not nullable,這樣就很難拒絕某列為空值的記錄。
4.外部表不能使用continueif ,如果記錄有換行的就比較難處理。