SPOOL的用法


舉例:test.sh

#! /bin/sh

sqlplus -s scott/tiger<<EOF
 set head off
 set linesize 20000
 set echo off
 set feedback off
 set pagesize 0
 set termout off
 set trimout on
 set trimspool on
  spool /home/test.txt
   select empno || '|' || ename || '|' || job || '|' || 
   mgr || '|' || hiredate || '|' || sal || '|' || 
   comm || '|' || deptno from emp;
  spool off
  exit;
EOF

spool常用的設置:
set head off:輸出域標題,缺省為on
set linesize 20000:linesize可以設置的大點,防止一行長度不夠
set echo off:顯示sqlplus中的每個sql命令本身,缺省為on
set feedback off:回顯本次sql命令處理的記錄條數,缺省為on
set pagesize 0:輸出每頁行數,缺省為24,為了避免分頁,可設定為0
set termout off:顯示腳本中的命令的執行結果,缺省為on
set trimout on:去除標准輸出每行的拖尾空格,缺省為off
set trimspool on:去除重定向(spool)輸出每行的拖尾空格,缺省為off

 

要輸出符合要求格式的數據文件只需在select時用字符連接來規范格式。比如有如下表

SQL> select id,username,password from myuser;      //測試表
1 John       1234
2 Jack       12345
3 Rose       2345
4 Joe        384657
5 Tom        384655
6 Jordan     384455

要輸出符合1,John,1234,這樣的數據格式就用select id||','||username||','||password||',' from myuser這樣的語句。

SQL>; select id||','||username||','||password||',' from myuser;
1,John,1234,
2,Jack,12345,

寫個下面這樣的腳本就行可以輸出符合要求格式的數據至文件中,不會含有其它不需要東西,只有數據部分。

--腳本文件名為expmyusr.sql,存數據的文件名為e:\exp.txt

set echo on            --是否顯示執行的命令內容  
set feedback off       --是否顯示 *   rows   selected  
set heading off        --是否顯示字段的名稱
set verify off         --是否顯示替代變量被替代前后的語句。fil
set trimspool off      --去字段空格
set pagesize 1000      --頁面大小
set linesize 50//linesize設定盡量根據需要來設定,大了生成的文件也大
define fil= 'e:\exp.txt'
prompt *** Spooling to &fil
spool &fil
select id||','||username||','||'"'||password||'"' from myuser;
spool off;
--執行過程
SQL>; @e:\expmyusr.sql
*** Spooling to e:\exp.txt
1,John,"1234"
2,Jack,"12345"
3,Rose,"2345"
4,Joe,"384657"
5,Tom,"384655"
6,Jordan,"384455"

檢查可知結果符合要求。

Oracle SPOOL的兩種方法之對比:

通常情況下,我們使用SPOOL方法,將數據庫中的表導出為文本文件的時候會采用兩種方法,如下述:

方法一:采用以下格式腳本 

set colsep '' ------設置列分隔符
set trimspool on
set linesize 120
set pagesize 2000
set newpage 1
set heading off
set term off
spool 路徑+文件名
select * from tablename;
spool off

方法二:采用以下腳本

set trimspool on
set linesize 120
set pagesize 2000
set newpage 1
set heading off
set term off
spool 路徑+文件名
select col1||','||col2||','||col3||','||col4||'..' from tablename;
spool off

比較以上方法,

方法一采用設定分隔符然后由sqlplus自己使用設定的分隔符對字段進行分割,

方法二將分隔符拼接在SELECT語句中,即手工控制輸出格式。


在實踐中,我發現通過方法一導出來的數據具有很大的不確定性,這種方法導出來的數據再由sql ldr導入的時候出錯的可能性在95%以上,尤其?大批量的數據表,如100萬條記錄的表更是如此,而且導出的數據文件狂大。


而方法二導出的數據文件格式很規整,數據文件的大小可能是方法一的1/4左右。經這種方法導出來的數據文件再由sqlldr導入時,出錯的可能性很小,基本都可以導入成功。


因此,實踐中我建議大家使用方法二手工去控制spool文件的格式,這樣可以減小出錯的可能性,避免走很多彎路。

補充1--參數說明:

------spool------

set arraysize 5000;  --此參數可提高SPOOL卸載的速度,最大可以設置為5000--

set autotrace on;    --設置允許對執行的sql進行分析--

set colsep ',';   --域輸出分隔符--

set echo off;    --顯示start啟動的腳本中的每個sql命令,缺省為on--

set feedback off;  --回顯本次sql命令處理的記錄條數,缺省為on,設置顯示“已選擇XX行”--

set heading off;   --輸出域標題,字段的名稱,缺省為on--

SET LINESIZE 2500;   
--每行允許的最大字符數,設置大些,免得數據被截斷,但不宜過大,太大會大大降低導出的速度(注意必須與trimspool結合使用防止導出的文本有太多的尾部空格)--

set newpage 1;       
--設置頁與頁之間的分隔{1|n|NONE};當值為0時在每頁開頭有一個小的黑方框;當值為n時在頁和頁之間隔着n個空行;當為none時,會在頁和頁之間沒有任何間隔;--

set newp none;       --設置查詢出來的數據分多少頁顯示,如果需要連續的數據,中間不要出現空行就把newp設置為none,這樣輸出的數據行都是連續的,中間沒有空行之類的--

set num 18;          --設置數字的長度,如果不夠大,則用科學記數法顯示--

set numwidth 12;    --輸出number類型域長度,缺省為10--

SET NULL text;       --顯示時,用text值代替NULL值--

set pagesize 2000;  --輸出每頁行數,頁面大小,缺省為24,為了避免分頁,可設定為0--

set serveroutput on; --設置允許顯示輸出類似dbms_output;--編寫存儲過程時,大多會將必要的信息輸出;--

SET SPACE 0;

set term off;        --不在屏幕上輸出執行結果--

set termout off;   --顯示腳本中的命令的執行結果,缺省為on--

set timing on;       --顯示每個sql語句花費的執行時間,設置顯示“已用時間:XXXX”--

set trimout on;   --去除標准輸出每行的拖尾空格,缺省為off--

set trimspool on;  --去除重定向(spool)輸出每行的拖尾空格,缺省為off--

set verify off       --是否顯示替代變量被替代前后的語句--

SET wrap on;         --輸出行長度大於設置行長度時(用set linesize n命令設置);值為on時,多余的字符另起一行顯示,否則多余的字符將被切除,不予顯示;--

--set markup html on;--
spool格式
spool d:/tables.csv    --指定輸出文件--

--也可以是 tables.html  或者 tables.xls ,但如果是html格式,要設置set markup html on  --

--  @d:/get_tables.sql      執行get_tables.sql文件,sql后綴默認可以不要--

select '"' || empno || '" ,"' || ename || '" ,"' || job || '","' || mgr || '","' || hiredate || '","' || sal || '","' || comm || '","' || deptno || '""'         
--指定SQL語句,如果是多個SQL語句的話,則要把語句寫入一個sql文件,用@(絕對路徑)sql文件名去執行,否則執行spool導出命令的話,sql語句也會寫到輸出文件去--

from scott.emp;   ---分隔為 ,用||連接字符和, ,--

spool off

exit

補充2:

今天實際項目中用到了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;

前面的設置是沒有用的,還是會在終端中顯示大量信息。

復制代碼

set echo off;
set heading off;
set line 100;
set long 2000000000;
set longchunksize 255;
set wra on;
set newpage none;
set pagesize 0;
set numwidth 12;
set termout off;
set trimout on;
set trimspool on;
set feedback off;
set timing on;
execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_1_4156_748575599.arc',Options=>dbms_logmnr.new);
execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_2_6645_748575599.arc',Options=>dbms_logmnr.addfile);
execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora');
spool /oracle/app/oracle/logs/record3.txt;
select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?'
from v$logmnr_contents;
spool off;
exit;

復制代碼

(2)那到底能否在shell腳本中運行還不顯示這些信息呢,答案是有的。
例如

復制代碼

#!/bin/ksh
echo "set echo off;
set heading off;
set line 100;
set long 2000000000;
set longchunksize 255;
set wra on;
set newpage none;
set pagesize 0;
set numwidth 12;
set termout off;
set trimout on;
set trimspool on;
set feedback off;
set timing on;
execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/hrbfct_1_4156_748575599.arc',Options=>dbms_logmnr.new);
execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora');
spool /oracle/app/oracle/logs/record3.txt;
select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?'
from v\$logmnr_contents;
spool off;
" | sqlplus '/as sysdba'>/dev/null

復制代碼

這樣就能利用shell腳本中執行spool方法,同時不會再終端中顯示。注意,只有這種方法可以。

試過這種方法,結果證明是不行的。。。。  看着和上面echo進去很像,但事實就是不行,還是會顯示大量的信息,兩個!就是將中間內容發送到sqlplus中作為輸入

復制代碼

#!/bin/bash
......
.....
sqlplus oracleuser/user@SERVICE_NAME << !
set ECHO off
set heading off
set pagesize 0
set linesize 1000
set term off
set trims on
set feedback off
spool $tmpfile
select owner||'.'||table_name||',' from all_tables where owner=upper('$owner_user') and table_name like 'DR%$exp_month%';
spool off
quit
!
.......

復制代碼

(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
復制代碼

set heading off
set feedback off
set echo off
set newp none
set termout off
spool /home/orarun/scripts/date.txt
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');
spool off;

復制代碼
 
        
復制代碼
 
         

#!/bin/sh

rm /home/orarun/scripts/date.txt

sqlplus tjc1/tjc10804@tjc1 << EOF

@selecttpmof03.txt #或者sqlplus 。。。。。。。。@logminer.sql

EOF

復制代碼
 
        

這種方法理論上也不會在終端上顯示信息,不知道為啥上面那個用!的就不行,感覺差不多的樣子

   (4)還有一種想法可以在shell腳本中編寫出一個.sql腳本,然后去執行它。

    

復制代碼
 

#!/bin/ksh
record=/oracle/app/oracle/logs/dirct
flag=0
count=1
echo "set echo off;
set heading off;
set line 100;
set long 2000000000;
set longchunksize 255;
set wra on;
set newpage none;
set pagesize 0;
set numwidth 12;
set termout off;
set trimout on;
set trimspool on;
set feedback off;
set timing on;" > logmnr.sql
echo "write config"
for file_i in `cat $record`;
do
flag=1
if [ $count -eq 1 ];then
sed -i '/'''$file_i'''/d' $record
echo "execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/$file_i',Options=>dbms_logmnr.new);">>logmnr.sql
count=0
else
sed -i '/'''$file_i'''/d' $record
echo "execute dbms_logmnr.add_logfile(LogFileName=>'/oracle/app/oracle/logs/$file_i',Options=>dbms_logmnr.addfile);">>logmnr.sql
fi
done
echo "execute dbms_logmnr.start_logmnr(DictFileName=>'/oracle/app/oracle/logs/dict.ora');">>logmnr.sql
if [ $flag -eq 1 ];then
echo "spool /oracle/app/oracle/logs/record3.txt;
select to_clob(sql_redo)||'|'||to_char(scn)||'|'||to_char(timestamp)||'|'||to_char(session_info)||'|'||to_char(table_name)||'|'||to_char(seg_owner)||'?'
from v\$logmnr_contents;
spool off;
exit;">>logmnr.sql
#sqlplus '/as sysdba' @logmnr.sql


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM