パラレルDMLは、並列実行メカニズムを利用して大規模データベースのテーブルやインデックスに対する挿入、更新、削除などの操作の実行効率を向上させます。意思決定支援システム(DSS)向けのデータベースにおいて、パラレルDMLはクエリと更新機能を提供し、パラレルクエリ機能を補完します。OLTPデータベースにおいては、パラレルDML操作によりバッチ処理ジョブの実行が高速化されます。
パラレルDMLの有効化と無効化
OceanBaseデータベースでは、SQLステートメントまたはセッション内でパラレルDMLを明示的に有効にすることができます。
SQLステートメントでのパラレルDMLの有効化と無効化
SQLステートメントでパラレルDMLを有効にするには、ステートメント内に以下のヒントを挿入します:
/*+ ENABLE_PARALLEL_DML PARALLEL(3) */
通常、ENABLE_PARALLEL_DML ヒントと PARALLEL ヒントは組み合わせて使用する必要があります。ただし、ターゲットテーブルのスキーマでテーブルレベルの並列度が指定されている場合は、ENABLE_PARALLEL_DML ヒントのみを指定する必要があります。
以下の例では、ENABLE_PARALLEL_DML ヒントと PARALLEL(n) パラメータを同時に使用して、並列度 n を指定しています。ここで n > 1 で、並列度 dop=2 です。
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT) NOPARALLEL;
CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 11 PARTITION BY HASH(c1) PARTITIONS 3;
CREATE TABLE t3 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 10 PARTITION BY HASH(c1) PARTITIONS 4;
obclient> EXPLAIN INSERT /*+ ENABLE_PARALLEL_DML PARALLEL(2) */ INTO t1 SELECT * FROM T3;
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Query Plan |
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| ========================================================================= |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------------------------------- |
| |0 |OPTIMIZER STATS MERGE | |1 |18 | |
| |1 | PX COORDINATOR | |1 |18 | |
| |2 | EXCHANGE OUT DISTR |:EX10001 |1 |18 | |
| |3 | INSERT | |1 |17 | |
| |4 | EXCHANGE IN DISTR | |1 |4 | |
| |5 | EXCHANGE OUT DISTR (HASH)|:EX10000 |1 |4 | |
| |6 | OPTIMIZER STATS GATHER | |1 |4 | |
| |7 | SUBPLAN SCAN |ANONYMOUS_VIEW1|1 |4 | |
| |8 | PX BLOCK ITERATOR | |1 |4 | |
| |9 | TABLE SCAN |t3 |1 |4 | |
| ========================================================================= |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output(nil), filter(nil), rowset=256 |
| 1 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil), rowset=256 |
| 2 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil), rowset=256 |
| dop=2 |
| 3 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil) |
| columns([{t1: ({t1: (t1.c1, t1.c2)})}]), partitions(p0), |
| column_values([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]) |
| 4 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil), rowset=256 |
| 5 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil), rowset=256 |
| (#keys=1, [column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)]), dop=2 |
| 6 - output([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.c1)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.c2)]), filter(nil), rowset=256 |
| 7 - output([ANONYMOUS_VIEW1.c1], [ANONYMOUS_VIEW1.c2]), filter(nil), rowset=256 |
| access([ANONYMOUS_VIEW1.c1], [ANONYMOUS_VIEW1.c2]) |
| 8 - output([t3.c1], [t3.c2]), filter(nil), rowset=256 |
| 9 - output([t3.c1], [t3.c2]), filter(nil), rowset=256 |
| access([t3.c1], [t3.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([t3.c1]), range(MIN ; MAX)always true |
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
34 rows in set
パラレルDMLを無効にするには、ステートメント内に以下のヒントを挿入します:
/*+ DISABLE_PARALLEL_DML */
セッションでパラレルDMLが有効になっていても、特定のSQLステートメント内で DISABLE_PARALLEL_DML ヒントを使用してパラレルDMLを無効にできます。
セッションでのパラレルDMLの有効化と無効化
デフォルトでは、SQLステートメントで PARALLEL ヒントを使用しても、パラレルDMLは有効になりません。そのため、セッション単位でパラレルDMLを有効にする必要があります。
MySQLモードでセッション内のパラレルDMLを有効にする構文は次のとおりです:
SET _FORCE_PARALLEL_DML_DOP = n;
ここで、n は 1 より大きい値です。
Oracleモードでセッション内のパラレルDMLを有効にする構文は次のとおりです:
ALTER SESSION ENABLE PARALLEL DML;
Oracleモードでは、セッション内でパラレルDMLを強制的に有効にする場合は、以下のSQLステートメントを実行します:
ALTER SESSION FORCE PARALLEL DML PARALLEL n;
以下の例は、Oracleモードでセッション内でパラレルDMLを強制的に有効にする方法です。
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT) NOPARALLEL;
CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 11 PARTITION BY HASH(c1) PARTITIONS 3;
CREATE TABLE t3 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 10 PARTITION BY HASH(c1) PARTITIONS 4;
obclient> ALTER SESSION FORCE PARALLEL DML PARALLEL 6;
Query OK, 0 rows affected
obclient> EXPLAIN INSERT INTO t2 SELECT * FROM t3;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Query Plan |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ============================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------------------------------------ |
| |0 |OPTIMIZER STATS MERGE | |1 |16 | |
| |1 | PX COORDINATOR | |1 |16 | |
| |2 | EXCHANGE OUT DISTR |:EX10001 |1 |15 | |
| |3 | INSERT | |1 |15 | |
| |4 | EXCHANGE IN DISTR | |1 |2 | |
| |5 | EXCHANGE OUT DISTR (PKEY HASH)|:EX10000 |1 |2 | |
| |6 | OPTIMIZER STATS GATHER | |1 |2 | |
| |7 | SUBPLAN SCAN |ANONYMOUS_VIEW1|1 |2 | |
| |8 | PX BLOCK ITERATOR | |1 |2 | |
| |9 | TABLE SCAN |T3 |1 |2 | |
| ============================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output(nil), filter(nil), rowset=256 |
| 1 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| 2 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| dop=6 |
| 3 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil) |
| columns([{T2: ({T2: (T2.C1, T2.C2)})}]), partitions(p[0-2]), |
| column_values([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]) |
| 4 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)], [PARTITION_ID]), filter(nil), |
| rowset=256 |
| 5 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)], [PARTITION_ID]), filter(nil), |
| rowset=256 |
| (#keys=1, [column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)]), dop=6 |
| 6 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| 7 - output([ANONYMOUS_VIEW1.C1], [ANONYMOUS_VIEW1.C2]), filter(nil), rowset=256 |
| access([ANONYMOUS_VIEW1.C1], [ANONYMOUS_VIEW1.C2]) |
| 8 - output([T3.C1], [T3.C2]), filter(nil), rowset=256 |
| 9 - output([T3.C1], [T3.C2]), filter(nil), rowset=256 |
| access([T3.C1], [T3.C2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([T3.C1]), range(MIN ; MAX)always true |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
36 rows in set
注意点として、SQLステートメントでパラレルDMLを有効にする場合、一般的にヒントで指定された並列度でクエリが実行されます。この優先順位は、セッション内で強制的に指定された並列度よりも高いです。Oracleモードの例は以下のとおりです:
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT) NOPARALLEL;
CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 11 PARTITION BY HASH(c1) PARTITIONS 3;
CREATE TABLE t3 (c1 INT PRIMARY KEY, c2 INT) PARALLEL 10 PARTITION BY HASH(c1) PARTITIONS 4;
obclient> ALTER SESSION FORCE PARALLEL DML PARALLEL 6;
Query OK, 0 rows affected
obclient> EXPLAIN INSERT /*+ PARALLEL(3) */ INTO t2 SELECT * FROM t3;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Query Plan |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ============================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------------------------------------ |
| |0 |OPTIMIZER STATS MERGE | |1 |17 | |
| |1 | PX COORDINATOR | |1 |17 | |
| |2 | EXCHANGE OUT DISTR |:EX10001 |1 |17 | |
| |3 | INSERT | |1 |16 | |
| |4 | EXCHANGE IN DISTR | |1 |3 | |
| |5 | EXCHANGE OUT DISTR (PKEY HASH)|:EX10000 |1 |3 | |
| |6 | OPTIMIZER STATS GATHER | |1 |3 | |
| |7 | SUBPLAN SCAN |ANONYMOUS_VIEW1|1 |3 | |
| |8 | PX BLOCK ITERATOR | |1 |3 | |
| |9 | TABLE SCAN |T3 |1 |3 | |
| ============================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output(nil), filter(nil), rowset=256 |
| 1 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| 2 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| dop=3 |
| 3 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil) |
| columns([{T2: ({T2: (T2.C1, T2.C2)})}]), partitions(p[0-2]), |
| column_values([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]) |
| 4 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)], [PARTITION_ID]), filter(nil), |
| rowset=256 |
| 5 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)], [PARTITION_ID]), filter(nil), |
| rowset=256 |
| (#keys=1, [column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)]), dop=3 |
| 6 - output([column_conv(NUMBER,PS:(-1,0),NOT NULL,ANONYMOUS_VIEW1.C1)], [column_conv(NUMBER,PS:(-1,0),NULL,ANONYMOUS_VIEW1.C2)]), filter(nil), rowset=256 |
| 7 - output([ANONYMOUS_VIEW1.C1], [ANONYMOUS_VIEW1.C2]), filter(nil), rowset=256 |
| access([ANONYMOUS_VIEW1.C1], [ANONYMOUS_VIEW1.C2]) |
| 8 - output([T3.C1], [T3.C2]), filter(nil), rowset=256 |
| 9 - output([T3.C1], [T3.C2]), filter(nil), rowset=256 |
| access([T3.C1], [T3.C2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([T3.C1]), range(MIN ; MAX)always true |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------+
36 rows in set
パラレルDMLを無効にする場合、MySQLモードのSQLステートメントは次のとおりです:
SET _FORCE_PARALLEL_DML_DOP = 1;
パラレルDMLを無効にする場合、OracleモードのSQLステートメントは次のとおりです:
ALTER SESSION DISABLE PARALLEL DML;
パラレルDMLを無効にすると、SQLステートメントで PARALLEL ヒントを使用しても、パラレルDMLは実行されません。 セッションでパラレルDMLを有効にすると、そのセッション内のすべてのDMLステートメントに対してパラレル実行が適用されます。SQLステートメントが ENABLE_PARALLEL_DML ヒントによってパラレルDMLを有効にした場合、パラレル実行は指定されたステートメントにのみ適用されます。ただし、パラレル属性を持つテーブルが存在しない場合や、パラレル操作の制限に違反した場合は、パラレルDMLが有効でもDML操作は直列実行されます。
セグメント間の並列処理をサポート
以下のSQLステートメントに基づいて、テーブルのパーティション並列処理機能を説明します。
テストテーブル
branch_sp_tbl_srcを作成します。CREATE TABLE branch_sp_tbl_src(id INT PRIMARY KEY, v INT) PARTITION BY KEY(id) PARTITIONS 4;テストテーブル
branch_sp_tbl_destを作成します。CREATE TABLE branch_sp_tbl_dest LIKE branch_sp_tbl_src;実行計画を確認します。
以下のSQLステートメントを実行すると、この挿入操作の実行方法が示されます。
obclient [test]> EXPLAIN BASIC INSERT /*+enable_parallel_dml parallel(100) query_timeout(1000000000)*/ INTO branch_sp_tbl_dest SELECT id, v FROM branch_sp_tbl_src ON DUPLICATE KEY UPDATE v = v + 1;実行結果は次のとおりです:
+-------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +-------------------------------------------------------------------------------------------------------------------------------------+ | ================================================ | | |ID|OPERATOR |NAME | | | ------------------------------------------------ | | |0 |PX COORDINATOR | | | | |1 |└─EXCHANGE OUT DISTR |:EX10000 | | | |2 | └─PX PARTITION ITERATOR| | | | |3 | └─INSERT_UP | | | | |4 | └─SUBPLAN SCAN |ANONYMOUS_VIEW1 | | | |5 | └─TABLE FULL SCAN|branch_sp_tbl_src| | | ================================================ | | Outputs & filters: | | ------------------------------------- | | 0 - output(nil), filter(nil), rowset=16 | | 1 - output(nil), filter(nil), rowset=16 | | dop=100 | | 2 - output(nil), filter(nil), rowset=16 | | partition wise, force partition granule | | 3 - output(nil), filter(nil) | | columns([{branch_sp_tbl_dest: ({branch_sp_tbl_dest: (branch_sp_tbl_dest.id, branch_sp_tbl_dest.v)})}]), partitions(p[0-3]), | | column_values([column_conv(INT,PS:(11,0),NOT NULL,ANONYMOUS_VIEW1.id)], [column_conv(INT,PS:(11,0),NULL,ANONYMOUS_VIEW1.v)]), | | update([branch_sp_tbl_dest.v=column_conv(INT,PS:(11,0),NULL,cast(branch_sp_tbl_dest.v + 1, INT(-1, 0)))]) | | 4 - output([ANONYMOUS_VIEW1.id], [ANONYMOUS_VIEW1.v]), filter(nil), rowset=16 | | access([ANONYMOUS_VIEW1.id], [ANONYMOUS_VIEW1.v]) | | 5 - output([branch_sp_tbl_src.id], [branch_sp_tbl_src.v]), filter(nil), rowset=16 | | access([branch_sp_tbl_src.id], [branch_sp_tbl_src.v]), partitions(p[0-3]) | | is_index_back=false, is_global_index=false, | | range_key([branch_sp_tbl_src.id]), range(MIN ; MAX)always true | +-------------------------------------------------------------------------------------------------------------------------------------+ 27 rows in setクエリプランにおける演算子の分析は以下のとおりです:
- 演算子0:これは並列実行コーディネーターを表し、並列実行のプロセスを管理します。
- 演算子1:データが異なる実行ノード間で配布されることを示します。
- 演算子2:クエリがパーティションを走査することを示します。「partition wise」とは、クエリが各パーティション間のデータをインテリジェントに処理することを意味します。
- 演算子3:挿入または更新操作を示します。挿入するキーがテーブルに存在しない場合は挿入操作を実行し、存在する場合は更新操作を実行します。
- 演算子4および5:
branch_sp_tbl_srcという名前のテーブルに対してフルテーブルスキャンを実行することを示します。このテーブルはデータソースであり、挿入操作のためのデータを選択します。
使用方法
OceanBaseデータベースは、以下のSQLステートメントの並列実行をサポートしています。
INSERT INTO SELECTUPDATEDELETE
テーブルに以下のインデックスタイプが存在する場合、並列実行をサポートする必要があります。
- ローカルインデックス
- シングルパーティション全体インデックス
- マルチパーティション全体インデックス
パラレルDMLがサポートするシナリオ
シナリオ |
INSERT |
UPDATE |
DELETE |
MERGE INTO |
|---|---|---|---|---|
テーブルに外部キー/TRIGGER(トリガー)/PL UDF(ユーザー定義関数)/一意インデックスが存在する
説明トリガーが実行されるかどうかは明確ではありません。トリガーがDML操作と無関係である場合、またはトリガーが実行されない場合、並列DMLが続行されます。例えば、 |
不可 | 不可 | 不可 | 不可 |
| 複数テーブルDML | INSERT ALL構文は、並列DMLをサポートしません |
UPDATEの並列処理はサポートします。 |
UPDATEと同じ | 構文はサポートされていません |
| 自動インクリメント列 | 特定のシナリオでは並列DMLをサポートします。詳細は以下の例を参照してください。 | サポート | サポート | サポート(seq) |
| ArrayBindingバッチ最適化 | 不可 | 不可 | 不可 | 不可 |
| USER_VARIABLEの使用 | 不可 | 不可 | 不可 | 不可 |
| IGNORE | 不可 | 不可 | 不可 | 構文はサポートされていません |
| DBLink | 不可 | 不可 | 不可 | 不可 |
| その他 | 以下のステートメントは並列DMLをサポートしません:
|
ON UPDATE CURRENT_TIMESTAMPを含む列はサポートされません |
INSERT の書き込み対象テーブルに自動インクリメント列がある場合、その自動インクリメント列が主キーまたはパーティションキーであり、かつその列が指定されているときは、パラレルDMLはサポートされません。
例:
テーブル
tbl1を作成します。列col1は自動インクリメント列であり、主キーとして指定されています。CREATE TABLE tbl1(col1 INT AUTO_INCREMENT PRIMARY KEY, col2 INT, col3 INT);テーブル
tbl2を作成します。列col1は自動インクリメント列です。CREATE TABLE tbl1(col1 INT AUTO_INCREMENT, col2 INT, col3 INT);テーブル
tbl3を作成します。CREATE TABLE tbl3(col1 INT, col2 INT, col3 INT);INSERTでデータを書き込む際、自動インクリメント列col1が主キーであるため、パラレルDMLはサポートされません。INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl1 SELECT * FROM tbl3;INSERTでデータを書き込む際、列col1が指定されていないため、パラレルDMLはサポートされます。INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl1(col2, col3) SELECT col2, col3 FROM tbl3;INSERTでデータを書き込む際、列col1が単なる自動インクリメント列であるため、パラレルDMLはサポートされます。INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl2 SELECT * FROM tbl3;
パラレルDMLによる関連更新
関連更新とは、UPDATE ステートメントで複数のテーブルを結合し、関連するテーブルのデータに基づいてターゲットテーブルを更新する操作を指します。パラレルDML機能を利用することで、大規模なデータ更新操作のパフォーマンスを大幅に向上させることができます。
例:
以下の手順では、パラレルDMLを使用して関連更新機能を実現する方法を示します。
テストテーブル
customersとordersを作成します。CREATE TABLE customers ( id INT PRIMARY KEY, name VARCHAR(50), customer_level VARCHAR(10) );CREATE TABLE orders ( order_id INT PRIMARY KEY, customer_id INT, discount DECIMAL(3, 2), amount DECIMAL(10, 2) );テストデータを挿入します。
customersテーブルに顧客データを挿入します:INSERT INTO customers (id, name, customer_level) VALUES (1, 'Alice', 'VIP'), (2, 'Bob', 'NORMAL'), (3, 'Charlie', 'OTHER');ordersテーブルに注文データを挿入します:INSERT INTO orders (order_id, customer_id, discount, amount) VALUES (101, 1, NULL, 1000.00), (102, 2, NULL, 2000.00), (103, 3, NULL, 1500.00), (104, 1, NULL, 3000.00), (105, 2, NULL, 2500.00);
パラレルDMLによる関連更新を実行します。
EXPLAINを使用して、パラレルDMLによる関連更新の実行計画を確認します。PARALLELヒントを使用して並列度を4に指定し、customersテーブルの顧客ランク(customer_level)に基づいて、ordersテーブルの割引フィールド(discount)を更新します。EXPLAIN UPDATE /*+ PARALLEL(4) */ orders o JOIN customers c ON o.customer_id = c.id SET o.discount = CASE WHEN c.customer_level = 'VIP' THEN 0.9 WHEN c.customer_level = 'NORMAL' THEN 0.95 ELSE 1.0 END;実行結果は次のとおりです:
+------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ========================================================================== | | |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | | -------------------------------------------------------------------------- | | |0 |DISTRIBUTED UPDATE | |3 |50 | | | |1 |└─PX COORDINATOR | |3 |3 | | | |2 | └─EXCHANGE OUT DISTR |:EX10001|3 |3 | | | |3 | └─SHARED HASH JOIN | |3 |3 | | | |4 | ├─EXCHANGE IN DISTR | |3 |2 | | | |5 | │ └─EXCHANGE OUT DISTR (BC2HOST)|:EX10000|3 |2 | | | |6 | │ └─PX BLOCK ITERATOR | |3 |1 | | | |7 | │ └─TABLE FULL SCAN |c |3 |1 | | | |8 | └─PX BLOCK ITERATOR | |5 |1 | | | |9 | └─TABLE FULL SCAN |o |5 |1 | | | ========================================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output(nil), filter(nil) | | table_columns([{o: ({orders: (o.order_id, o.customer_id, o.discount, o.amount)})}]), | | update([o.discount=column_conv(DECIMAL_INT,PS:(3,2),NULL,CASE WHEN c.customer_level = 'VIP' THEN cast(0.9, DECIMAL_INT(3, 2)) WHEN c.customer_level | | = 'NORMAL' THEN 0.95 ELSE cast(1.0, DECIMAL_INT(3, 2)) END)]) | | 1 - output([o.order_id], [o.customer_id], [o.discount], [o.amount], [c.customer_level]), filter(nil), rowset=16 | | 2 - output([o.order_id], [o.customer_id], [o.discount], [o.amount], [c.customer_level]), filter(nil), rowset=16 | | dop=4 | | 3 - output([o.order_id], [o.customer_id], [o.discount], [o.amount], [c.customer_level]), filter(nil), rowset=16 | | equal_conds([o.customer_id = c.id]), other_conds(nil) | | 4 - output([c.id], [c.customer_level]), filter(nil), rowset=16 | | 5 - output([c.id], [c.customer_level]), filter(nil), rowset=16 | | dop=4 | | 6 - output([c.id], [c.customer_level]), filter(nil), rowset=16 | | 7 - output([c.id], [c.customer_level]), filter(nil), rowset=16 | | access([c.id], [c.customer_level]), partitions(p0) | | is_index_back=false, is_global_index=false, | | range_key([c.id]), range(MIN ; MAX)always true | | 8 - output([o.order_id], [o.customer_id], [o.discount], [o.amount]), filter(nil), rowset=16 | | 9 - output([o.order_id], [o.customer_id], [o.discount], [o.amount]), filter(nil), rowset=16 | | access([o.order_id], [o.customer_id], [o.discount], [o.amount]), partitions(p0) | | is_index_back=false, is_global_index=false, | | range_key([o.order_id]), range(MIN ; MAX)always true | +------------------------------------------------------------------------------------------------------------------------------------------------------------+ 38 rows in set