oracle中使用plsql來進行平面檔案解除安裝
平面檔案解除安裝
要把資料從一個系統移動到另一個系統,如果沒有使用EXP/IMP或EXPDP/IMPDP(用於取代EXP和IMP的新資料泵),平面解除安裝就很有用.儘管使用EXP(DP)/IMP(DP)可以很好地將資料從一個系統移到另一個系統,但要求兩個系統都是Oracle.
jy@JINGYONG> create or replace package unloader
2 authid current_user
3 as
4 /* Funaction run--unloads data from any query into a file
5 and creates a control file to reload that
6 data into another table
7 p_query=SQL query to "unload".May be virtually and query.
8 p_tname=Table to load into.Will be put into control file.
9 p_mode=REPLACE|APPEND|TRUNCATE--how to reload the data
10 p_dir=directory we will write the ctl and dat file to.
11 p_filename=name of file to write to.I will add .ctl and .dat to this
12 name
13 p_separator=field delimiter. I default this to a comma.
14 p_enclosure=what each field will be wrapped in
15 p_terminator=end of line character. We use this so we can unload
16 and reload data with newlines in it. I default to
17 "|\n"(a pipe and a newline together) and "|\r\n" on NT.
18 You need only to override this if you believe your
19 data will have that sequence in it. I ALWAYS add the
20 OS "end of line" marker to this sequence,you should not
21 */
22 function run(p_query in varchar2,
23 p_tname in varchar2,
24 p_mode in varchar2 default 'REPLACE',
25 p_dir in varchar2,
26 p_filename in varchar2,
27 p_separator in varchar2 default ',',
28 p_enclosure in varchar2 default '"',
29 p_terminator in varchar2 default '|')
30 return number;
31 end;
32 /
程式包已建立。
注意這裡使用了AUTHID CURRENT_USER.這樣一來,這個包就可以在資料庫上只安裝一次,可由任何人用來解除安裝資料.要解除安裝數 據,只要求一點:對所解除安裝的表要有SELECT許可權,另外對這個包有EXECUTE許可權.如果這裡沒有使用AUTHID CURRENT_USER,則需 要這個包的所有者在要解除安裝的所有表上都有直接的SELECT許可權.
注意:SQL會以這個例程的呼叫者的許可權執行.不過,所有PL/SQL呼叫都會以所呼叫例程定義者的許可權執行;因此,對於具有這個 包執行許可權的所有人,都隱含地允許他使用UTL_FILE寫至一個目錄.
包體如下.我們使用UTL_FILE來寫一個控制檔案和一個資料檔案.DBMS_SQL用於動態地處理所有查詢.我們在查詢中使用了一個資料型別:VARCHAR2(4000).這說明,如果LOB大於4,000位元組,就不能使用這個方法來解除安裝LOB.不過,只需使用DBMS_LOB.SUBSTR,我們就可以使用這個方法解除安裝任何最多4,000位元組的LOB.另外,由於我們用一個VARCHAR2作為惟一的輸出資料型別,所以可以處理長度最多2,000位元組的RAW(4,000個十六進位制字元),除了LONG RAW和LOB外,這對其他型別都足夠了.另 外,如果查詢引用了一個非標量屬性(一個複雜的物件型別,巢狀表等),則不能使用這個簡單的實現.以下是一個90%可用的解決方案,這說明90%的情況下它都能解決問題:
create or replace package body unloader
as
g_theCursor integer default dbms_sql.open_cursor;
g_descTbl dbms_sql.desc_tab;
g_nl varchar2(2) default chr(10);
以上是這個包體中使用的一些全域性變數.全域性遊標開啟一次,即第一次引用這個包時開啟,它會一起開啟,直到我們登出.這就 不用每次呼叫這個包時都要得到一個新遊標,從而避免相應的開銷.G_DESCTBL是一個PL/SQL表,將儲存DBMS_SQL.DESCRIBE呼叫的輸出.G_NL是一個換行符.在需要內嵌有換行符的串中會使用這個變數.我們無需針對Windows調整這個變數,UTL_FILE會看到字串中的CHR(10),並自動為我們將其轉換為一個回車/換行符.
接下來,我們使用了一個很小的便利函式,它能將字元轉換為一個十六進位制數,為此使用了內建函式:
function to_hex(p_str in varchar2) return varchar2
is
begin
return to_char(ascii(p_str),'fm0x');
end;
最後,我們又建立了另一個便利函式IS_WINDOWS,它會返回TRUE或FALSE,這取決於我們所用的是否是Windows平臺,如果在 Windows平臺上,行結束符就是一個兩字元的串,而大多數其他平臺上的行結束符只是單字元.我們使用了內建DBMS_UTILITY函 數:GET_PARAMETER_VALUE,可以用這個函式讀取幾乎所有引數.我們獲取了CONTROL_FILES引數,並查詢其中是否存在\,如果 有,則說明在Windows平臺上:
function is_windows return boolean
is
l_cfiles varchar2(4000);
l_dummy number;
begin
if(dbms_utility.get_parameter_value('control_files',l_dummy,l_cfiles)>0) then
return instr(l_cfiles,'\')>0;
else
return FALSE;
end if;
end;
注意:IS_WINDOWS函式依賴於CONTROL_FILES引數中使用了\.要記住,其中也有可能使用/,但這極為少見.
下面的過程會建立一個控制檔案來重新載入解除安裝的資料,這裡使用了DBMS_SQL.DESCRIBE_COLUMN生成的DESCRIBE表.它會為我們處理有關作業系統的細節,如作業系統是否使用回車/換行符(用於STR屬性):
procedure dump_ctl(
p_dir in varchar2,
p_filename in varchar2,
p_tname in varchar2,
p_mode in varchar2,
p_separator in varchar2,
p_enclosure in varchar2,
p_terminator in varchar2)
is
l_output utl_file.file_type;
l_sep varchar2(5);
l_str varchar2(5):=chr(10);
begin
if(is_windows) then
l_str:=chr(13)||chr(10);
end if;
l_output:=utl_file.fopen(p_dir,p_filename|\'.ctl','w');
utl_file.put_line(l_output,'load data');
utl_file.put_line( l_output, 'infile ''' ||
p_filename || '.dat'' "str x''' ||
utl_raw.cast_to_raw( p_terminator ||
l_str ) || '''"' );
utl_file.put_line( l_output, 'into table ' || p_tname );
utl_file.put_line( l_output, p_mode );
utl_file.put_line( l_output, 'fields terminated by X''' ||
to_hex(p_separator) ||
''' enclosed by X''' ||
to_hex(p_enclosure) || ''' ' );
utl_file.put_line( l_output, '(' );
for i in 1 .. g_descTbl.count loop
if ( g_descTbl(i).col_type = 12 ) then
utl_file.put( l_output, l_sep || g_descTbl(i).col_name ||
' date ''ddmmyyyyhh24miss'' ');
else
utl_file.put( l_output, l_sep || g_descTbl(i).col_name ||
' char(' ||
to_char(g_descTbl(i).col_max_len*2) ||' )' );
end if;
l_sep := ','||g_nl ;
end loop;
utl_file.put_line( l_output, g_nl || ')' );
utl_file.fclose( l_output );
end;
這是一個簡單的函式,會返回一個加引號的串(使用所選擇的包圍字元作為引號);注意,串不只是包含字元,倘若串中還存在包圍字元,還會把包圍字元重複兩次,從而保留這些包圍字元:
function quote(p_str in varchar2,p_enclosure in varchar2)
return varchar2
is
begin
return p_enclosure||
replace(p_str,p_enclosure,p_enclosure||p_enclosure)||
p_enclosure;
end;
下面是主函式RUN.因為這個函式相當大,會列出函式的解釋:
function run(p_query in varchar2,
p_tname in varchar2,
p_mode in varchar2 default 'REPLACE',
p_dir in varchar2,
p_filename in varchar2,
p_separator in varchar2 default ',',
p_enclosure in varchar2 default '"',
p_terminator in varchar2 default '|')
return number
is
l_output utl_file.file_type;
l_columnValue varchar2(4000);
l_colCnt number default 0;
l_separator varchar2(10) default '';
l_cnt number default 0;
l_line long;
l_datefmt varchar2(255);
l_descTbl dbms_sql.desc_tab;
begin
我們將NLS_DATE_FORMAT儲存到一個變數中,從而在將資料轉儲到磁碟上時可以把它改為一種保留日期和時間的格式.採用這種方式,我們會保留日期的時間分量.然後建立一個異常塊,從而在接收到錯誤時重置NLS_DATE_FORMAT:
select value into l_datefmt
from nls_session_parameters where parameter='NLS_DATE_FORMAT';
/* 設定日期格式為是一個大數字字串避免所有的NLS問題並保留時間和日期 */
execute immediate 'alter session set nls_date_format="yyyymmddhh24miss"';
/* 設定一個異常塊在出現任何錯誤時重新設定日期格式 */
接下來,解析並描述這個查詢.將G_DESCTBL設定為L_DESCTBL來重置全域性表;否則,其中會包含前一個DESCRIBE生成的資料,而不只是當前查詢生成的資料.一旦完成,再呼叫DUMP_CTL具體建立控制檔案:
begin
/*
解析和描述這個查詢.將重設descTbl為了一個空表
*/
dbms_sql.parse( g_theCursor, p_query, dbms_sql.native );
g_descTbl := l_descTbl;
dbms_sql.describe_columns( g_theCursor, l_colCnt, g_descTbl );
/*
建立一個控制檔案來重新載入資料到你所期望的表中
*/
dump_ctl( p_dir, p_filename, p_tname, p_mode, p_separator,
p_enclosure, p_terminator );
/*
繫結每一個列轉換為一個長度為4000字串.我們不在乎我們獲取是數字還是日期,因為它們都可以
看成是一個字串
*/
現在可以將具體資料轉儲到磁碟上了.首先將每個列定義為VARCHAR2(4000)來獲取資料.所有型別(NUMBER,DATE,RAW)都要 轉換為VARCHAR2.在此之後,執行查詢來準備獲取:
for i in 1 .. l_colCnt loop
dbms_sql.define_column(g_theCursor,i,l_columnValue,4000);
end loop;
/*
執行這個查詢--忽略執行的輸出,它只是來驗證一個插入/更新或刪除操作
*/
現在開啟資料檔案準備寫,從查詢獲取所有行,並將其列印到資料檔案:
l_cnt := dbms_sql.execute(g_theCursor);
/*
開啟檔案然後將帶有分隔符的資料寫入到檔案中
*/
l_output := utl_file.fopen( p_dir, p_filename || '.dat', 'w',32760 );
loop
exit when ( dbms_sql.fetch_rows(g_theCursor) <= 0 );
l_separator := '';
l_line := null;
for i in 1 .. l_colCnt loop
dbms_sql.column_value( g_theCursor, i,l_columnValue );
l_line := l_line || l_separator ||quote( l_columnValue, p_enclosure );
l_separator := p_separator;
end loop;
l_line := l_line || p_terminator;
utl_file.put_line( l_output, l_line );
l_cnt := l_cnt+1;
end loop;
utl_file.fclose( l_output );
最後,將日期格式設定回原來的樣子(如果先前的程式碼由於某種原因失敗了,異常塊也會做這個工作),並返回:
/*
現在重新設定日期格式並將返回的行資料寫入到輸出檔案中
*/
execute immediate 'alter session set nls_date_format=''' || l_datefmt || '''';
return l_cnt;
exception
/*
如果發現任何錯誤會重新設定日期並重新觸發錯誤
*/
when others then
execute immediate 'alter session set nls_date_format=''' || l_datefmt || '''';
RAISE;
end;
end run;
end unloader;
執行完整的編譯
SQL> create or replace package body unloader
2 as
3 g_theCursor integer default dbms_sql.open_cursor;
4 g_descTbl dbms_sql.desc_tab;
5 g_nl varchar2(2) default chr(10);
6
7 function to_hex(p_str in varchar2) return varchar2
8 is
9 begin
10 return to_char(ascii(p_str),'fm0x');
11 end;
12
13 function is_windows return boolean
14 is
15 l_cfiles varchar2(4000);
16 l_dummy number;
17 begin
18 if(dbms_utility.get_parameter_value('control_files',l_dummy,l_cfiles)>0) then
19 return instr(l_cfiles,'\')>0;
20 else
21 return FALSE;
22 end if;
23 end;
24
25 procedure dump_ctl(
26 p_dir in varchar2,
27 p_filename in varchar2,
28 p_tname in varchar2,
29 p_mode in varchar2,
30 p_separator in varchar2,
31 p_enclosure in varchar2,
32 p_terminator in varchar2)
33 is
34 l_output utl_file.file_type;
35 l_sep varchar2(5);
36 l_str varchar2(5):=chr(10);
37 begin
38 if(is_windows) then
39 l_str:=chr(13)||chr(10);
40 end if;
41 l_output:=utl_file.fopen(p_dir,p_filename||'.ctl','w');
42 utl_file.put_line(l_output,'load data');
43 utl_file.put_line( l_output, 'infile ''' ||
44 p_filename || '.dat'' "str x''' ||
45 utl_raw.cast_to_raw( p_terminator ||
46 l_str ) || '''"' );
47 utl_file.put_line( l_output, 'into table ' || p_tname );
48 utl_file.put_line( l_output, p_mode );
49 utl_file.put_line( l_output, 'fields terminated by X''' ||
50 to_hex(p_separator) ||
51 ''' enclosed by X''' ||
52 to_hex(p_enclosure) || ''' ' );
53 utl_file.put_line( l_output, '(' );
54
55 for i in 1 .. g_descTbl.count loop
56 if ( g_descTbl(i).col_type = 12 ) then
57 utl_file.put( l_output, l_sep || g_descTbl(i).col_name ||
58 ' date ''ddmmyyyyhh24miss'' ');
59 else
60 utl_file.put( l_output, l_sep || g_descTbl(i).col_name ||
61 ' char(' ||
62 to_char(g_descTbl(i).col_max_len*2) ||' )' );
63 end if;
64 l_sep := ','||g_nl ;
65 end loop;
66 utl_file.put_line( l_output, g_nl || ')' );
67 utl_file.fclose( l_output );
68 end;
69
70 function quote(p_str in varchar2,p_enclosure in varchar2)
71 return varchar2
72 is
73 begin
74 return p_enclosure||
75 replace(p_str,p_enclosure,p_enclosure||p_enclosure)||
76 p_enclosure;
77 end;
78
79
80 function run(p_query in varchar2,
81 p_tname in varchar2,
82 p_mode in varchar2 default 'REPLACE',
83 p_dir in varchar2,
84 p_filename in varchar2,
85 p_separator in varchar2 default ',',
86 p_enclosure in varchar2 default '"',
87 p_terminator in varchar2 default '|')
88 return number
89 is
90 l_output utl_file.file_type;
91 l_columnValue varchar2(4000);
92 l_colCnt number default 0;
93 l_separator varchar2(10) default '';
94 l_cnt number default 0;
95 l_line long;
96 l_datefmt varchar2(255);
97 l_descTbl dbms_sql.desc_tab;
98 begin
99 select value into l_datefmt
100 from nls_session_parameters where parameter='NLS_DATE_FORMAT';
101 /* 設定日期格式為是一個大數字字串避免所有的NLS問題並保留時間和日期 */
102 execute immediate 'alter session set nls_date_format="yyyymmddhh24miss"';
103 /* 設定一個異常塊在出現任何錯誤時重新設定日期格式 */
104 begin
105 /*
106 解析和描述這個查詢.將重設descTbl為了一個空表
107 */
108 dbms_sql.parse( g_theCursor, p_query, dbms_sql.native );
109 g_descTbl := l_descTbl;
110 dbms_sql.describe_columns( g_theCursor, l_colCnt, g_descTbl );
111 /*
112 建立一個控制檔案來重新載入資料到你所期望的表中
113 */
114 dump_ctl( p_dir, p_filename, p_tname, p_mode, p_separator,
115 p_enclosure, p_terminator );
116 /*
117 繫結每一個列轉換為一個長度為4000字串.我們不在乎我們獲取是數字還是日期,因為它們都可以
118 看成是一個字串
119 */
120 for i in 1 .. l_colCnt loop
121 dbms_sql.define_column(g_theCursor,i,l_columnValue,4000);
122 end loop;
123 /*
124 執行這個查詢--忽略執行的輸出,它只是來驗證一個插入/更新或刪除操作
125 */
126 l_cnt := dbms_sql.execute(g_theCursor);
127 /*
128 開啟檔案然後將帶有分隔符的資料寫入到檔案中
129 */
130 l_output := utl_file.fopen( p_dir, p_filename || '.dat', 'w',32760 );
131 loop
132 exit when ( dbms_sql.fetch_rows(g_theCursor) <= 0 );
133 l_separator := '';
134 l_line := null;
135 for i in 1 .. l_colCnt loop
136 dbms_sql.column_value( g_theCursor, i,l_columnValue );
137 l_line := l_line || l_separator ||quote( l_columnValue, p_enclosure );
138 l_separator := p_separator;
139 end loop;
140 l_line := l_line || p_terminator;
141 utl_file.put_line( l_output, l_line );
142 l_cnt := l_cnt+1;
143 end loop;
144 utl_file.fclose( l_output );
145 /*
146 現在重新設定日期格式並將返回的行資料寫入到輸出檔案中
147 */
148 execute immediate 'alter session set nls_date_format=''' || l_datefmt || '''';
149 return l_cnt;
150 exception
151 /*
152 如果發現任何錯誤會重新設定日期並重新觸發錯誤
153 */
154 when others then
155 execute immediate 'alter session set nls_date_format=''' || l_datefmt || '''';
156 RAISE;
157 end;
158 end run;
159 end unloader;
160 /
Package body created
要執行這個程式碼,可以使用以下命令(要注意,當然以下程式碼需要你將SCOTT.EMP的SELECT許可權授予某個角色,或者直接授予你 自己):
jy@JINGYONG> set serveroutput on
jy@JINGYONG> create or replace directory dir1 as '/home/oracle';
目錄已建立。
jy@JINGYONG> declare
2 l_rows number;
3 begin
4 l_rows:=unloader.run
5 (p_query=>'select * from scott.emp order by empno',
6 p_tname=>'emp',
7 p_mode=>'replace',
8 p_dir=>'DIR1',
9 p_filename=>'emp',
10 p_separator=>',',
11 p_enclosure=>'"',
12 p_terminator=>'~');
13 dbms_output.put_line(to_char(l_rows)||' rows extracted to ascii file');
14 end;
15 /
14 rows extracted to ascii file
PL/SQL 過程已成功完成。
由此生成的emp.ctl控制檔案顯示如下(注意,括號裡粗體顯示的數字並不是真的包括在檔案中:加上這些數字只是為了便於引 用):
[oracle@jy ~]$ cat emp.ctl
load data
infile 'emp.dat' "str x'7E0A'"
into table emp
replace
fields terminated by X'2c' enclosed by X'22'
(
EMPNO char(44 ),
ENAME char(20 ),
JOB char(18 ),
MGR char(44 ),
HIREDATE date 'ddmmyyyyhh24miss' ,
SAL char(44 ),
COMM char(44 ),
DEPTNO char(44 )
)
關於這個控制檔案,要注意以下幾點:
第2行:使用了SQLLDR的STR特性.可以指定用什麼字元或串來結束一個記錄.這樣就能很容易地載入有內嵌換行符的資料.串x '7E0A'只是換行符後面跟一個波浪號"~".
第5行:使用了我們的分隔符和包圍符.這裡沒有使用OPTIONALLY ENCLOSED BY,因為我們將把原資料中包圍字元的所有出現都 重複兩次,再把每個欄位括起來.
第11行:使用了一個很大的"數值"日期格式.這有兩個作用:可以避免與日期有關的所有NLS問題,還可以保留日期欄位的時間分量.
從前面的程式碼生成的原始資料(emp.dat)檔案如下:
[oracle@jy ~]$ cat emp.dat
"7369","SMITH","CLERK","7902","19801217000000","800","","20"~
"7499","ALLEN","SALESMAN","7698","19810220000000","1600","300","30"~
"7521","WARD","SALESMAN","7698","19810222000000","1250","500","30"~
"7566","JONES","MANAGER","7839","19810402000000","2975","","20"~
"7654","MARTIN","SALESMAN","7698","19810928000000","1250","1400","30"~
"7698","BLAKE","MANAGER","7839","19810501000000","2850","","30"~
"7782","CLARK","MANAGER","7839","19810609000000","2450","","10"~
"7788","SCOTT","ANALYST","7566","19870419000000","3000","","20"~
"7839","KING","PRESIDENT","","19811117000000","5000","","10"~
"7844","TURNER","SALESMAN","7698","19810908000000","1500","0","30"~
"7876","ADAMS","CLERK","7788","19870523000000","1100","","20"~
"7900","JAMES","CLERK","7698","19811203000000","950","","30"~
"7902","FORD","ANALYST","7566","19811203000000","3000","","20"~
"7934","MILLER","CLERK","7782","19820123000000","1300","","10"~
emp.dat檔案中要注意的問題如下:
每個欄位都用包圍字元括起來.
DATE解除安裝為很大的數字>
這個檔案中的資料行按要求以一個~結束>
現在可以使用SQLLDR很容易地重新載入這個資料,你可以向SQLLDR命令列增加你認為合適的選項.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-773789/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- adobe安裝出錯原來是cloud殘留檔案沒解除安裝Cloud
- Solaris中對tar.z進行安裝解除安裝教程詳解
- [Linux]檔案掛載和解除安裝Linux
- oracle 19c 安裝、解除安裝Oracle
- cocoapods安裝/解除安裝/使用
- Oracle11g解除安裝Oracle
- 如何在 Linux 上安裝/解除安裝一個檔案中列出的軟體包?Linux
- python中的pip的安裝以及通過pip命令對selenium進行解除安裝和安裝Python
- windows安裝plsqlWindowsSQL
- 二進位制檔案安裝安裝etcd
- win10有的檔案解除安裝不了怎麼解決 win10有些檔案解除安裝不了如何處理Win10
- PLSQL安裝與使用問題彙總SQL
- plsql以及instantclient下載安裝配置使用SQLclient
- Oracle RAC叢集解除安裝步驟Oracle
- oracle下載安裝及PLSQL連線資料庫教程OracleSQL資料庫
- window下安裝並使用nvm(含解除安裝node、解除安裝nvm、全域性安裝npm)NPM
- oracle plsqlOracleSQL
- linux下oracle安裝檔案.cpio.gz檔案的解壓LinuxOracle
- revit解除安裝/完美解決安裝失敗/如何徹底解除安裝清除乾淨revit各種殘留登錄檔和檔案的方法
- 在Oracle Linux 7.1中使用原始碼來安裝PostgreSQL 9.6OracleLinux原始碼SQL
- 在Linux中,掛載和解除安裝檔案系統過程是什麼?Linux
- Oracle安裝光碟內容的檔案說明Oracle
- macOS 徹底解除安裝 Adobe XD 及清理相關檔案Mac
- 在不安裝oracle客戶端的情況下,使用PLSQLOracle客戶端SQL
- oracle11g的安裝,及plsql developer11的安裝、漢化和註冊OracleSQLDeveloper
- plsql developer 15的安裝配置SQLDeveloper
- DLL解除安裝(建立遠端執行緒解除安裝強制注入的dll)執行緒
- linux環境下解除安裝oracle11gLinuxOracle
- Oracle Linux 7使用syslog來管理Oracle ASM的審計檔案OracleLinuxASM
- 使用apt install安裝本地deb檔案APT
- linux下如何使用檔案來模擬硬碟進行ASM測試Linux硬碟ASM
- win10怎麼解除安裝無用檔案_win10如何清理無用檔案Win10
- node使用multer進行檔案上傳
- Docker中安裝OracleDockerOracle
- Linux安裝解除安裝MySQLLinuxMySql
- Ubuntu解除安裝和安裝Ubuntu
- JDK安裝和解除安裝JDK
- docker安裝及解除安裝Docker
- 【PSU】Oracle打PSU及解除安裝PSU(單例項)Oracle單例