CSVに含まれる一行の中に、説明書きとして以下のようなのがあります。
1.20 from tokyo to london
これをパースしてデータベースに保存したいんだけど、と同僚より相談を受けました。JavaScriptだとできるんだけど、PL/SQLは書いたことがない、とのことでした。
それだと、何から始めて良いのかわからないので、ざくっとOracle APEXでサンプルを作って、こうやるのだよ、と伝えました。その作業記録です。
まず、スキーマをクイックSQLで定義しました。
# prefix: szx
# semantics: default
raw_data
body file
csv_data
raw_data_id /fk raw_data
category vc4000
description vc4000
data_transfers
csv_data_id /fk csv_data
fare num
from_place vc255
to_place vc255
regexps
category vc80
regexp_str vc4000
insert_str vc4000
var_num num
seq num
CSVファイルはSZX_ROW_DATA表のBLOBにアップロードします。このクイックSQLの定義から、特別な指定をせずにスキーマの生成からアプリケーションの作成までを行います。
CSVファイルをアップロードする際に、ファイル名やファイルのタイプなどもデータベースにきちんと登録させるために、フォームに含まれるBLOBのページ・アイテムに、MIMETYPE列、ファイル名列、文字セット列、BLOB最終更新列を設定します。
CSVファイルをアップロードしたのちに、カンマで分割する処理をアップロードしたファイルの一覧ページに追加します。
PL/SQLコードでは、APEX_DATA_PARSER.PARSEを使用しています。
begin
-- id number should be changed or supplied as a parameter
for c in (select id, body from szx_raw_data)
loop
delete from szx_csv_data where raw_data_id = c.id;
insert into szx_csv_data(raw_data_id, category, description)
select c.id, col001, col002 from
apex_data_parser.parse(
p_content => c.body,
p_file_name => 'data.csv'
);
end loop;
end;
次にDESCRIPTIONの列をパースする処理を、CSVファイルのパース結果の一覧画面に追加します。
PL/SQLコードでは、regexp_replaceを使用しています。
declare
l_matched varchar2(4000);
l_cursor integer;
l_rows_processed integer;
l_idx varchar2(4);
l_vals apex_t_varchar2;
l_idxstr varchar2(16);
begin
for c1 in (select * from szx_csv_data)
loop
-- dbms_output.put_line(c1.category || ' = ' || c1.description);
for c2 in (select * from szx_regexps where category = c1.category order by seq)
loop
l_idxstr := '';
for i in 1..c2.var_num
loop
l_idxstr := l_idxstr || '\' || i || ':';
end loop;
-- dbms_output.put_line(c2.regexp_str || ' = ' || c2.insert_str || ' = ' || l_idxstr);
l_matched := regexp_replace(c1.description, c2.regexp_str, l_idxstr);
if l_matched <> c1.description then
-- dbms_output.put_line('MATCHED!!!');
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse(l_cursor, c2.insert_str, dbms_sql.native);
dbms_sql.bind_variable(l_cursor, ':1', c1.id);
l_vals := apex_string.split(l_matched, ':');
for i in 2..c2.var_num
loop
l_idx := ':' || i;
dbms_sql.bind_variable(l_cursor, l_idx, l_vals(i-1));
end loop;
l_rows_processed := dbms_sql.execute(l_cursor);
dbms_sql.close_cursor(l_cursor);
end if;
end loop;
end loop;
commit;
end;
正規表現と、それが一致した時に実行するINSERT文を表に登録します。
登録する正規表現その1
カテゴリ: fare
正規表現: ([0-9\.]+)\s+from\s+([A-Za-z ]+)\s+to\s+([A-Za-z ]+)
INSERT文: insert into szx_data_transfers(csv_data_id, fare, from_place, to_place) values(:1, :2, :3, :4)
バインド変数の数: 4
登録する正規表現その2
カテゴリ: fare
正規表現: ([0-9\.]+)\s+to\s+([A-Za-z ]+)\s+from\s+([A-Za-z ]+)
INSERT文: insert into szx_data_transfers(csv_data_id, fare, from_place, to_place) values(:1, :2, :4, :3)
バインド変数の数: 4
以上です。テストには以下のCSVファイルをしました。
fare, 1.200 from tokyo to london
fare, 3200.00 from beijing to tokyo
fare, 4500.00 to london from new york
fare, 900.00 to new york from beijing
アプリケーションのエクスポートを、こちらに置いています。
https://github.com/ujnak/apexapps/blob/master/exports/csvparser.sql
完




