最近、DBMS_CLOUD.EXPORT_DATAとパッケージAPEX_DATA_EXPORTを使ったCSVファイルへのエクスポートについて比較したので、ついでにDBMS_CLOUD.COPY_DATAとパッケージAPEX_DATA_LOADINGも比較してみました。また、外部表を使用したインポートも行ってみました。
速度についてはDBMS_CLOUD.COPY_DATA(および外部表を使ったインポート)が高速です。大量のデータをインポートする場合は、こちらの手順を使うことになると思います。
テストに使用するデータを準備します。こちらの記事と同じ環境でテストを行います。
表BRICKSよりオブジェクト・ストレージに、10,000,000行のデータをエクスポートします。オブジェクト名はbricks_10m_で始まるようにします。同様に100,000行のデータをエクスポートします。オブジェクト名はbricks_100k_で始まるようにします。
オブジェクト・ストレージへのエクスポートには、以下のスクリプトを使用します。
declare | |
C_REGION constant varchar2(20) := 'us-ashburn-1'; | |
C_NAMESPACE constant varchar2(20) := 'ネームスペースに置き換える'; | |
C_BUCKET constant varchar2(20) := 'download'; | |
$IF false $THEN -- for DBMS_CLOUD.COPY_DATA | |
C_FILENAME constant varchar2(20) := 'bricks_10m'; | |
C_SQL constant varchar2(4000) := q'~select * from bricks where rownum < 10000001~'; | |
$ELSE -- for APEX_DATA_LOADING | |
C_FILENAME constant varchar2(20) := 'bricks_100k'; | |
C_SQL constant varchar2(4000) := q'~select * from bricks where rownum < 100001~'; | |
$END | |
l_path varchar2(400); | |
l_operation_id number; | |
l_status varchar2(9); | |
procedure set_checkpoint(ids varchar2) | |
as | |
begin | |
dbms_output.put_line(systimestamp || ': ' || ids); | |
dbms_session.set_identifier('csv_export-' || ids); | |
end; | |
begin | |
SET_CHECKPOINT('dbms_cloud.export_data'); | |
l_path := 'https://objectstorage.' || C_REGION || '.oraclecloud.com/n/' || C_NAMESPACE | |
|| '/b/' || C_BUCKET || '/o/' || C_FILENAME; | |
dbms_cloud.export_data | |
( | |
credential_name => 'DEF_CRED' | |
, file_uri_list => l_path | |
, format => json_object( | |
'type' value 'csv' | |
, 'maxfilesize' value '214783648' | |
, 'compression' value 'gzip' | |
) | |
, query => C_SQL | |
, operation_id => l_operation_id | |
); | |
SET_CHECKPOINT('end'); | |
for i in 1..10 | |
loop | |
select status into l_status from user_load_operations where id = l_operation_id; | |
SET_CHECKPOINT(l_status); | |
if l_status = 'COMPLETED' then | |
exit; | |
end if; | |
dbms_session.sleep(1); | |
end loop; | |
end; | |
/ |
$IFのフラグをtrueとfalseに変更し、それぞれ1回ずつスクリプトを実行すると以下のように、オブジェクトが2つ、指定したバケット以下に作成されます。
DBMS_CLOUD.COPY_DATAを使ったデータ・ロード
SQL> create table bricks_imp as select * from bricks where 1<>1;
Table BRICKS_IMPは作成されました。
SQL> desc bricks_imp
名前 Nullかどうか タイプ
______________ ___________ _______________
BRICK_ID NOT NULL NUMBER
COLOUR NOT NULL VARCHAR2(6)
SHAPE NOT NULL VARCHAR2(8)
WEIGHT NOT NULL NUMBER
INSERT_DATE NOT NULL DATE
JUNK NOT NULL VARCHAR2(50)
SQL>
DBMS_CLOUD.COPY_DATAを使って、データをロードするスクリプトは以下になります。
declare | |
C_REGION constant varchar2(20) := 'us-ashburn-1'; | |
C_NAMESPACE constant varchar2(20) := 'ネームスペースに置き換える'; | |
C_BUCKET constant varchar2(20) := 'download'; | |
C_FILENAME constant varchar2(80) := 'bricks_10m_'; | |
l_path varchar2(400); | |
l_operation_id number; | |
l_status varchar2(9); | |
procedure set_checkpoint(ids varchar2) | |
as | |
begin | |
dbms_output.put_line(systimestamp || ': ' || ids); | |
dbms_session.set_identifier('csv_import-' || ids); | |
end; | |
begin | |
SET_CHECKPOINT('dbms_cloud.copy_data'); | |
l_path := 'https://objectstorage.' || C_REGION || '.oraclecloud.com/n/' || C_NAMESPACE | |
|| '/b/' || C_BUCKET || '/o/' || C_FILENAME || '*'; | |
dbms_cloud.copy_data | |
( | |
table_name => 'BRICKS_IMP' | |
, credential_name => 'DEF_CRED' | |
, file_uri_list => l_path | |
, format => json_object( | |
'type' value 'csv' | |
, 'compression' value 'gzip' | |
, 'characterset' value 'AL32UTF8' | |
, 'dateformat' value 'DD-MM-RR' | |
) | |
, operation_id => l_operation_id | |
); | |
SET_CHECKPOINT('end'); | |
for i in 1..10 | |
loop | |
select status into l_status from user_load_operations where id = l_operation_id; | |
SET_CHECKPOINT(l_status); | |
if l_status in ('COMPLETED','FAILED') then | |
exit; | |
end if; | |
dbms_session.sleep(1); | |
end loop; | |
end; | |
/ |
10,000,000行のロードにかかっている時間は、大体30秒といったところです。
SQL> set serveroutput on
SQL> set time on timing on
13:18:47 SQL> @copy_data
22-04-04 04:18:50.369347000 +00:00: dbms_cloud.copy_data
22-04-04 04:19:22.944414000 +00:00: end
22-04-04 04:19:22.953348000 +00:00: COMPLETED
PL/SQLプロシージャが正常に完了しました。
経過時間: 00:00:32.951
経過時間: 00:00:33.122
13:19:23 SQL> select count(*) from bricks_imp;
COUNT(*)
___________
10000000
経過時間: 00:00:00.439
13:19:38 SQL> select * from bricks
2 minus
3* select * from bricks_imp;
行が選択されていません
経過時間: 00:00:36.553
13:20:27 SQL>
表BRICKS_IMPからすべての行を削除して、再度データのロードを実行しました。処理時間にそれほどの違いは出ていません。
13:20:27 SQL> delete from bricks_imp;
10,000,000行削除されました。
経過時間: 00:00:26.661
13:23:37 SQL> commit;
コミットが完了しました。
経過時間: 00:00:00.354
13:23:41 SQL> @copy_data
22-04-04 04:23:45.554795000 +00:00: dbms_cloud.copy_data
22-04-04 04:24:17.105518000 +00:00: end
22-04-04 04:24:17.106193000 +00:00: COMPLETED
PL/SQLプロシージャが正常に完了しました。
経過時間: 00:00:31.905
経過時間: 00:00:32.080
13:24:17 SQL>
セグメントのサイズを確認してみました。
13:24:17 SQL> select bytes from user_segments where segment_name = 'BRICKS_IMP';
BYTES
___________
51380224
経過時間: 00:00:00.447
13:28:11 SQL> truncate table bricks_imp;
Table BRICKS_IMPが切り捨てられました。
経過時間: 00:00:02.720
13:28:25 SQL> select bytes from user_segments where segment_name = 'BRICKS_IMP';
BYTES
________
65536
経過時間: 00:00:00.342
13:28:29 SQL> @copy_data
22-04-04 04:28:34.204638000 +00:00: dbms_cloud.copy_data
22-04-04 04:29:06.123755000 +00:00: end
22-04-04 04:29:06.124399000 +00:00: COMPLETED
PL/SQLプロシージャが正常に完了しました。
経過時間: 00:00:32.442
経過時間: 00:00:32.615
13:29:06 SQL> select bytes from user_segments where segment_name = 'BRICKS_IMP';
BYTES
___________
26214400
経過時間: 00:00:00.344
13:29:12 SQL>
外部表を使ったデータ・ロード
次のスクリプトを実行して、外部表BRICKS_EXTを作成します。
declare | |
C_REGION constant varchar2(20) := 'us-ashburn-1'; | |
C_NAMESPACE constant varchar2(20) := 'ネームスペースに置き換える'; | |
C_BUCKET constant varchar2(20) := 'download'; | |
C_FILENAME constant varchar2(80) := 'bricks_10m_'; | |
l_path varchar2(400); | |
begin | |
l_path := 'https://objectstorage.' || C_REGION || '.oraclecloud.com/n/' || C_NAMESPACE | |
|| '/b/' || C_BUCKET || '/o/' || C_FILENAME || '*'; | |
dbms_cloud.create_external_table( | |
credential_name => 'DEF_CRED' | |
, table_name => 'bricks_ext' | |
, file_uri_list => l_path | |
, format => json_object( | |
'type' value 'csv' | |
, 'dateformat' value 'DD-MM-RR' | |
, 'compression' value 'auto' | |
) | |
, column_list => | |
'brick_id number, | |
colour varchar2(6), | |
shape varchar2(8), | |
weight number, | |
insert_date date, | |
junk varchar2(50)' | |
); | |
end; | |
/ |
13:44:20 SQL> create table bricks_imp as select * from bricks_ext;
Table BRICKS_IMPは作成されました。
経過時間: 00:00:33.023
13:45:08 SQL>
13:59:54 SQL> delete from bricks_imp;
10,000,000行削除されました。
経過時間: 00:00:26.859
14:00:31 SQL> commit;
コミットが完了しました。
経過時間: 00:00:00.365
14:00:36 SQL> select bytes from user_segments where segment_name = 'BRICKS_IMP';
BYTES
___________
26214400
経過時間: 00:00:00.352
14:00:38 SQL> insert into bricks_imp select * from bricks_ext;
10,000,000行挿入しました。
経過時間: 00:00:30.709
14:01:35 SQL> commit;
コミットが完了しました。
経過時間: 00:00:00.525
14:02:30 SQL> select bytes from user_segments where segment_name = 'BRICKS_IMP';
BYTES
___________
59768832
経過時間: 00:00:00.350
14:02:34 SQL>
INSERT SELECTでも処理時間はそれほど変わりません。インサート後のセグメントのサイズが倍増していることより、こちらもダイレクト・パス・インサートになっているようです。
APEX_DATA_LOADINGを使ったデータ・ロード
1,blue,cylinder,2,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2,blue,pyramid,8,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3,blue,prism,3,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
4,blue,cube,3,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5,blue,cylinder,4,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6,blue,pyramid,2,01-JAN-11,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
アプリケーション・ビルダーに移ります。
declare | |
C_REGION constant varchar2(20) := 'us-ashburn-1'; | |
C_NAMESPACE constant varchar2(20) := 'ネームスペースに置き換える'; | |
C_BUCKET constant varchar2(20) := 'download'; | |
C_FILENAME constant varchar2(80) := 'bricks_100k_1_20220404T034543Z.csv.gz'; | |
l_path varchar2(400); | |
l_blob blob; | |
l_load_result apex_data_loading.t_data_load_result; | |
procedure set_checkpoint(ids varchar2) | |
as | |
begin | |
dbms_output.put_line(systimestamp || ': ' || ids); | |
dbms_session.set_identifier('csv_import-' || ids); | |
end; | |
begin | |
l_path := 'https://objectstorage.' || C_REGION || '.oraclecloud.com/n/' || C_NAMESPACE | |
|| '/b/' || C_BUCKET || '/o/' || C_FILENAME; | |
SET_CHECKPOINT('dbms_cloud.get_object'); | |
l_blob := dbms_cloud.get_object( | |
credential_name => 'DEF_CRED' | |
, object_uri => l_path | |
, compression => 'AUTO' | |
); | |
dbms_output.put_line('Length: ' || dbms_lob.getlength(l_blob)); | |
SET_CHECKPOINT('apex_session.create_session'); | |
apex_session.create_session( | |
p_app_id => 100 | |
, p_page_id => 1 | |
, p_username => 'APEXDEV' | |
); | |
SET_CHECKPOINT('apex_data_loading.load_data'); | |
l_load_result := apex_data_loading.load_data( | |
p_static_id => 'BRICKS' | |
, p_data_to_load => l_blob | |
); | |
SET_CHECKPOINT('end'); | |
end; | |
/ |
16:42:05 SQL> alter session set NLS_LANGUAGE = 'American';
Sessionが変更されました。
経過時間: 00:00:00.526
16:42:20 SQL> @import
22-04-04 07:42:22.534203000 +00:00: dbms_cloud.get_object
Length: 8094330
22-04-04 07:42:22.807388000 +00:00: apex_session.create_session
22-04-04 07:42:22.818248000 +00:00: apex_data_loading.load_data
22-04-04 07:50:31.205545000 +00:00: end
PL/SQLプロシージャが正常に完了しました。
経過時間: 00:08:09.042
経過時間: 00:08:09.219
16:50:31 SQL>