數據庫有如下表結構:
aaa (
obj_id NUMBER, (Primary Key)
obj_name VARCHAR2(80),
obj_size NUMBER NOT NULL
obj_time NUMBER NOT NULL
ms_version NUMBER NOT NULL
)
現在有100萬條記錄,要求每1000條記錄導出爲一個csv文件,共計1000個文件,文件名稱格式爲output0.csv,output1.csv......output999.csv。每個csv文件每行記錄包含:$1, $2, $3, $4四部分內容。
$1 = obj_id|obj_name
$2 = obj_size *10+obj_version
$3 = obj_time in format YYYY-MM-DD HH:mi
$4 = obj_name
要求第一個文件在500ms以內產生出來,後面每個文件生成間隔時間不得大於500ms,速度均勻。方案一:使用pl/sql存儲過程分頁查詢,每次查詢1000條記錄寫一個文件,實現代碼如下:
- create or replace procedure export_to_csv(P_DIR IN VARCHAR2)
- is
- CSV_OUTPUT UTL_FILE.FILE_TYPE;
- MAX_LINE NUMBER := 1000;
- OUT_FILE_NAME varchar2(20);
- OBJ_SIZE NUMBER;
- MIN_RECORD NUMBER;
- MAX_RECORD NUMBER;
- OBJ_DATE varchar2(100);
- BEGIN_TIME NUMBER;
- END_TIME NUMBER;
- begin
- BEGIN_TIME := dbms_utility.get_time;--記錄開始時間,注意dbms_utility.get_time獲取的時間戳單位是1/100s
- FOR I IN 0..999 loop
- OUT_FILE_NAME := 'output' || I ||'.csv';--拼接文件名
- MIN_RECORD := i*1000 + 1;--分頁查詢起始記錄
- MAX_RECORD := (i+1)*1000;--分頁查詢中止記錄
- --以寫方式打開指定目錄中指定文件名文件
- CSV_OUTPUT := UTL_FILE.FOPEN(P_DIR, OUT_FILE_NAME, 'W', MAX_LINE);
- --隱式遊標,每次查詢1000條記錄
- FOR cur IN (SELECT * FROM(SELECT A.*, ROWNUM rn FROM(SELECT * FROM aaa)A WHERE ROWNUM <= MAX_RECORD) WHERE rn >= MIN_RECORD) LOOP
- OBJ_SIZE := cur.OBJ_SIZE*10 + cur.MS_VERSION;
- --將數值類型數據轉換爲日期字符串
- OBJ_DATE := TO_CHAR(TO_DATE('19700101','yyyymmdd') + cur.OBJ_TIME/86400,'yyyy-MM-dd HH24:mi');
- --寫文件
- UTL_FILE.PUT_LINE(CSV_OUTPUT,cur.OBJ_ID || '|' || cur.OBJ_NAME || ',' || OBJ_SIZE || ',' || OBJ_DATE || ',' || cur.OBJ_NAME);
- END LOOP;
- UTL_FILE.FCLOSE(CSV_OUTPUT);
- END LOOP;
- END_TIME := dbms_utility.get_time;
- DBMS_output.put_line('Total time=' || (END_TIME-BEGIN_TIME)*10 || 'ms.');
- END;
- /
- begin
- export_to_csv('MYDIR');
- end;
- /
經過測試,1000個文件總共花150s,平均每個文件150ms。
注意:執行該存儲過程之前,UTL_FILE的目錄必須以sysdba的用戶創建,然後授權給使用的用戶,代碼中的MYDIR通過下面方法創建:
首先,以sysdba用戶登錄。
其次,創建目錄,如:CREATE DIRECTORY MYDIR AS 'c:\oraload\';
最後,給用戶授權,如:GRANT READ,WRITE ON DIRECTORY MYDIR TO scott;
方案二:部分頁,一次性將100萬條記錄全部查詢出來放到遊標中,每1000條寫一個文件,代碼如下:
- create or replace procedure export_to_csv(P_DIR IN VARCHAR2)
- is
- --顯示遊標,一次性將數據全部查詢完
- cursor mycur is select * from aaa;
- --行記錄
- myrecord aaa%rowtype;
- CSV_OUTPUT UTL_FILE.FILE_TYPE;
- MAX_LINE NUMBER := 1000;
- OUT_FILE_NAME varchar2(20);
- OBJ_SIZE NUMBER;
- OBJ_DATE varchar2(100);
- BEGIN_TIME NUMBER;
- END_TIME NUMBER;
- COUNT_NUM NUMBER;
- begin
- BEGIN_TIME := dbms_utility.get_time;
- --顯式打開遊標
- open mycur;
- FOR I IN 0..999 loop
- --拼接文件名
- OUT_FILE_NAME := 'output' || I ||'.csv';
- COUNT_NUM := 0;
- --打開文件
- CSV_OUTPUT := UTL_FILE.FOPEN(P_DIR, OUT_FILE_NAME, 'W', MAX_LINE);
- --每1000條寫一個文件
- while COUNT_NUM < 1000 loop
- --逐條叫遊標記錄放入記錄中
- fetch mycur into myrecord;
- OBJ_SIZE := myrecord.OBJ_SIZE*10 + myrecord.MS_VERSION;
- OBJ_DATE := TO_CHAR(TO_DATE('19700101','yyyymmdd') + myrecord.OBJ_TIME/86400,'yyyy-MM-dd HH24:mi');
- UTL_FILE.PUT_LINE(CSV_OUTPUT,myrecord.OBJ_ID || '|' || myrecord.OBJ_NAME || ',' || OBJ_SIZE || ',' || OBJ_DATE || ',' || myrecord.OBJ_NAME);
- COUNT_NUM := COUNT_NUM+1;
- --取遊標中下一條記錄
- fetch mycur into myrecord;
- END LOOP;
- UTL_FILE.FCLOSE(CSV_OUTPUT);
- END LOOP;
- --關閉遊標
- close mycur;
- END_TIME := dbms_utility.get_time;
- DBMS_output.put_line('Total time=' || (END_TIME-BEGIN_TIME)*10 || 'ms.');
- END;
- /
- begin
- export_to_csv('MYDIR');
- end;
- /