Thursday, January 18, 2007

utl_file csv output

[質問]
以下のパッケージを作成しておけば、select文によって表のデータをCSV形式で
ファイルに出力させることが可能です。
各表ごとにパッケージを作成する必要はありません。

なお、本パッケージはR8i以降でご使用いただけます。

[回答]
以下にパッケージを作成するスクリプト(mkCSV.sql)を示します。
注:スクリプト内の、<ファイルを出力するディレクトリを指定>には適切なディレクトリ
を指定してください。
(なお、本スクリプトはお客様の便宜のために公開しているものですので、スクリプト
自体に対するご質問はご遠慮下さいますようお願い申し上げます。)

---------- cut ---------------------- cut -------------- cut --------------
CREATE OR REPLACE PACKAGE print_csv_pack
AS

FUNCTION print_csv_exe(v_rowid in varchar2, v_owner in varchar2, v_table in varchar2)
RETURN varchar2;

FUNCTION print_csv(v_rowid in varchar2, v_rownum in number)
RETURN varchar2;

-- 引数のROWIDの行が存在する表の所有者名
v_owner varchar2(30);

-- 引数のROWIDの行が存在する表名
v_object_name varchar2(30);

END print_csv_pack;
/
show error

CREATE OR REPLACE PACKAGE BODY print_csv_pack
AS

/* 引数の表の各行の値をCSV形式でファイルに出力するファンクション */
FUNCTION print_csv_exe
(v_rowid in varchar2,
v_owner in varchar2,
v_table in varchar2)
RETURN varchar2
IS
file_handl utl_file.file_type;

-- dba_tab_columnsより、引数の表の列名を取得
cursor dba_tab_columns_cur is
select column_name
from sys.dba_tab_columns
where owner = v_owner and table_name = v_table;

-- 1列目には','を挿入しないため、1列目かどうかを判定するフラグ
loop_flag number := 1;

-- 動的に作成されるSQL文を格納する変数
sql_text varchar2(2000);

-- 各列の値
column_value varchar2(32767);

-- CSV形式で列値をつなげた文字列
column_values varchar2(32767);
BEGIN
file_handl := utl_file.fopen( '<ファイルを出力するディレクトリを指定>','csv.out','a',32767);

for dba_tab_columns_cur_rec in dba_tab_columns_cur loop

-- dba_tab_columnsのcolumn_nameの値を使用し、引数の表の各列値を取得するSELECT文を作成
sql_text := 'select ' || dba_tab_columns_cur_rec.column_name
|| ' from ' || v_owner || '.' || v_table ||
' where rowid = ''' || v_rowid || '''';

-- 作成したSQL文を実行
execute immediate sql_text into column_value;

-- 取得した列値を','をはさんで接続し、CSV形式の文字列を作成
if loop_flag = 1 then
column_values := column_values || column_value;
loop_flag := 0;
else
column_values := column_values || ',' || column_value;
end if;
end loop;

utl_file.put_line(file_handl, column_values);
utl_file.fclose(file_handl);

return 'Success!!';
END print_csv_exe;


/* 引数のROWIDの行が存在する表を特定し、特定した表を引数としてprint_csv_exeを実行する
ファンクション */
FUNCTION print_csv
(v_rowid in varchar2, v_rownum in number)
RETURN varchar2
IS
-- CSV形式で列値をつなげた文字列
column_values varchar2(32767);

BEGIN
-- rownum=1の場合のみ、所有者名、表名を取得
if v_rownum = 1 then
select owner, object_name into v_owner, v_object_name
from sys.dba_objects
where object_id = dbms_rowid.rowid_object(v_rowid);
end if;

-- print_csv_exeを実行し、CSV形式で列値をつなげた文字列を取得
column_values := print_csv_exe(v_rowid, v_owner, v_object_name);

return column_values;
END print_csv;

END print_csv_pack;
/
show error
---------- cut ---------------------- cut -------------- cut --------------


scott.empをCSV形式で出力する例を示します。

1.初期化パラメータutl_file_dirに、ファイルを出力させるディレクトリを設定して
インスタンスを起動します。

2.以下の手順で上記パッケージを実行します。

SQL> connect / as sysdba
SQL> grant select on dba_tab_columns to scott; <== パッケージを作成するスキーマには、
SQL> grant select on dba_objects to scott; <== 両ビューのselect権限を個別に与えて
SQL> connect scott/tiger おく必要があります。
SQL> @mkCSV.sql
SQL> select print_csv_pack.print_csv(rowid, rownum) from scott.emp
where empno >= 7800;

PRINT_CSV_PACK.PRINT_CSV(ROWID,ROWNUM)
--------------------------------------------------------------------------------
Success!!
Success!!
Success!!
Success!!
Success!!
Success!!

以下、csv.outファイルの内容。

7839,KING,PRESIDENT,,81-11-17,5000,,10
7844,TURNER,SALESMAN,7698,81-09-08,1500,0,30
7876,ADAMS,CLERK,7788,83-01-12,1100,,20
7900,JAMES,CLERK,7698,81-12-03,950,,30
7902,FORD,ANALYST,7566,81-12-03,3000,,20
7934,MILLER,CLERK,7782,82-01-23,1300,,10

No comments: