今天實際項目中用到了spool,發現網上好多內容不是很全,自己摸索了好半天,現在總結一下。
一、通過spool 命令,可以將select 數據庫的內容寫到文件中,通過在sqlplus設置一些參數,使得按指定方式寫到文件中
(1)常規使用spool方法,將set的一些命令和spool,select等放入.sql腳本中,然后再sqlplus中運行該腳本。以下為logmnr.sql腳本,
在sqlplus中執行@logmnr.sql就可以寫入文件record3.txt中。不會再終端顯示任何信息。但是,如果是在sqlplus中輸入:
set termout off;
......
spool record3.txt
select ....... from .....;
spool off;
前面的設置是沒有用的,還是會在終端中顯示大量信息。
1 set echo off; 2 set heading off; 3 set line 100; 4 set long 2000000000; 5 set longchunksize 255; 6 set wra on; 7 set newpage none; 8 set pagesize 0; 9 set numwidth 12; 10 set termout off; 11 set trimout on; 12 set trimspool on; 13 set feedback off; 14 set timing on; 15 execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_1_4156_748575599.arc',Options=>dbms_logmnr.new); 16 execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_2_6645_748575599.arc',Options=>dbms_logmnr.addfile); 17 execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora'); 18 spool /oracle/app/oracle/logs/record3.txt; 19 select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?' 20 from v$logmnr_contents; 21 spool off; 22 exit;
(2)那到底能否在shell腳本中運行還不顯示這些信息呢,答案是有的。
例如
1 #!/bin/ksh 2 echo "set echo off; 3 set heading off; 4 set line 100; 5 set long 2000000000; 6 set longchunksize 255; 7 set wra on; 8 set newpage none; 9 set pagesize 0; 10 set numwidth 12; 11 set termout off; 12 set trimout on; 13 set trimspool on; 14 set feedback off; 15 set timing on; 16 execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_1_4156_748575599.arc',Options=>dbms_logmnr.new); 17 execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora'); 18 spool /oracle/app/oracle/logs/record3.txt; 19 select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?' 20 from v\$logmnr_contents; 21 spool off; 22 " | sqlplus '/as sysdba'>/dev/null
這樣就能利用shell腳本中執行spool方法,同時不會再終端中顯示。注意,只有這種方法可以。
試過這種方法,結果證明是不行的。。。。 看着和上面echo進去很像,但事實就是不行,還是會顯示大量的信息,兩個!就是將中間內容發送到sqlplus中作為輸入
1 #!/bin/bash 2 ...... 3 ..... 4 sqlplus oracleuser/user@SERVICE_NAME << ! 5 set ECHO off 6 set heading off 7 set pagesize 0 8 set linesize 1000 9 set term off 10 set trims on 11 set feedback off 12 spool $tmpfile 13 select owner||'.'||table_name||',' from all_tables where owner=upper('$owner_user') and table_name like 'DR%$exp_month%'; 14 spool off 15 quit 16 ! 17 .......
(2)spool通常會用到連接||,這里講一下連接是怎么回事
SQL> SELECT LPAD('x',4000,'x') || LPAD('x',4000,'x') || LPAD('x',4000,'x') FROM DUAL; SELECT LPAD('x',4000,'x') || LPAD('x',4000,'x') || LPAD('x',4000,'x') FROM DUAL * ERROR at line 1: ORA-01489: result of string concatenation is too long
這里簡單先介紹下lpad和rpad是怎么回事:(l,r只是方向不同)
rpad函數從右邊對字符串使用指定的字符進行填充
rpad(string,padded_length,[pad_string])
string 表示:被填充的字符串
padded_length 表示:字符的長度,是返回的字符串的數量,如果這個數量比原字符串的長度要短,rpad函數將會把字符串截取成從左到右的n個字符;
pad_string 是個可選參數,這個字符串是要粘貼到string的右邊,如果這個參數未寫,lpad函數將會在string的右邊粘貼空格。
例如:
rpad('tech', 7); 將返回'tech '
rpad('tech', 2); 將返回'te'
rpad('tech', 8, '0'); 將返回'tech0000'
rpad('tech on the net', 15, 'z'); 將返回 'tech on the net'
rpad('tech on the net', 16, 'z'); 將返回 'tech on the netz'
好了,現在回到上面的問題,為什么會出錯呢,因為varchar2在oracle中,最多只支持到4000個字符,也就是32K,||的操作會把后面的放入到前面里,就是把后面4000個x放入到
前一個4000個x里,作為varchar2,當然就超過了4000的界限。
Problem Description:
The problem with this query is with the use of CONCAT operator (||).
e.g.: select char1 || char2 from dual
Concat operator returns char1 concatenated with char2. The string returned is in the
same character set as char1. So here concat operator is trying to return varchar2,
which has limit of 4000 characters and getting exceeded.
This problem may also come when we try to CONCAT a VARCHAR2 with CLOB.
e.g.: select char1 || clob from dual
So here we can simply convert its first string to CLOB and avoid this error.
After converting first string to CLOB, CONCAT operator will return string of CLOB type
Solution:
SELECT TO_CLOB(LPAD('x',4000,'x')) || LPAD('x',4000,'x') || LPAD('x',4000,'x')
FROM DUAL
所以問題解決了,只需要將連接的第一個轉換成clob就可以。
看我上面的logminer.sql,我將sql_redo用to_clob函數轉換成了clob類型,如果不設置set long 20000000和set longchunksize 255;就會發現,在record3文檔中每行只有
前80個字符,剩下的都被截斷了,這就是我上篇博客中的clob截斷問題,所以用上篇博客的方法可以完美解決問題。歐耶!
(3)shell調用spool 的另一種方法,
那就是在shell中調用
selecttpmof03.txt
1 set heading off 2 set feedback off 3 set echo off 4 set newp none 5 set termout off 6 spool /home/orarun/scripts/date.txt 7 select a.REC_CREATOR||'|'||a.REC_CREATE_TIME||'|'||b.event_name||'|'||a.ORDER_NO||'|'||a.MAT_NO||'|'||a.MAT_STATUS||'|'||a.WT from tpmof03 a,tpmof21 b where (a.event_id=b.event_id and a.event_id in('52','6A','6B','6C','6D','5B'))and (a.rec_create_time>='20120101000000'); 8 spool off;
1 #!/bin/sh 2 3 rm /home/orarun/scripts/date.txt 4 5 sqlplus tjc1/tjc10804@tjc1 << EOF 6 7 @selecttpmof03.txt #或者sqlplus 。。。。。。。。@logminer.sql 8 9 EOF
這種方法理論上也不會在終端上顯示信息,不知道為啥上面那個用!的就不行,感覺差不多的樣子
(4)還有一種想法可以在shell腳本中編寫出一個.sql腳本,然后去執行它。
1 #!/bin/ksh 2 record=/oracle/app/oracle/logs/dirct 3 flag=0
4 count=1
5 echo "set echo off;
6 set heading off; 7 set line 100; 8 set long 2000000000; 9 set longchunksize 255; 10 set wra on; 11 set newpage none; 12 set pagesize 0; 13 set numwidth 12; 14 set termout off; 15 set trimout on; 16 set trimspool on; 17 set feedback off; 18 set timing on;" > logmnr.sql
19 echo "write config"
20 for file_i in `cat $record`; 21 do
22 flag=1
23 if [ $count -eq 1 ];then
24 sed -i '/'''$file_i'''/d' $record 25 echo "execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/$file_i',Options=>dbms_logmnr.new);">>logmnr.sql 26 count=0
27 else
28 sed -i '/'''$file_i'''/d' $record 29 echo "execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/$file_i',Options=>dbms_logmnr.addfile);">>logmnr.sql 30 fi
31 done
32 echo "execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora');">>logmnr.sql 33 if [ $flag -eq 1 ];then
34 echo "spool /oracle/app/oracle/logs/record3.txt;
35 select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?'
36 from v\$logmnr_contents; 37 spool off; 38 exit;">>logmnr.sql
39 #sqlplus '/as sysdba' @logmnr.sql