---
title: "并行 DML - OceanBase 数据库 V4.6.0 | OceanBase 文档中心"
description: 并行 DML 并行 DML 通过使用并行执行机制来提高对大型数据库的表和索引执行插入、更新、删除等操作，以提高执行效率。对于决策支持系统 (DSS)的数据库，并行 DML 提供了查询和更新功能，是对并行查询功能的补充。对于 OLTP 数据库，并行 DML 操作可以加速批处理作业的运行。 开启和关闭并行 DML Oce…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 4.6.0

# 并行 DML

更新时间：2026-06-18 19:24:58

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V4.6.0/zh-CN/700.reference/1000.performance-tuning-guide/500.sql-optimization/300.distributed-execution-plan/1000.parallel-dml.md)  

并行 DML 通过使用并行执行机制来提高对大型数据库的表和索引执行插入、更新、删除等操作，以提高执行效率。对于决策支持系统 (DSS)的数据库，并行 DML 提供了查询和更新功能，是对并行查询功能的补充。对于 OLTP 数据库，并行 DML 操作可以加速批处理作业的运行。

## 开启和关闭并行 DML

OceanBase 数据库支持在 SQL 语句或会话中显式启用并行 DML。

### 在 SQL 语句中启用和关闭并行 DML

在 SQL 语句中启用并行 DML，请在语句中插入如下 Hint：

```sql
/*+ ENABLE_PARALLEL_DML PARALLEL(3) */

```

一般情况下，`ENABLE_PARALLEL_DML` Hint 和 `PARALLEL` Hint 必须配合使用才能开启并行 DML。不过，当目标表的 Schema 上指定了表级别的并行度时，仅需指定 `ENABLE_PARALLEL_DML` Hint。

如下示例为同时使用 `ENABLE_PARALLEL_DML` Hint 和 `PARALLEL(n)` 参数指定并行度 `n`，并且 `n > 1`，并行度 `dop=2`。

```sql
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，请在语句中插入如下 Hint：

```sql
/*+ DISABLE_PARALLEL_DML */

```

即使会话中启用了并行 DML，您也可以在指定 SQL 语句中使用 `DISABLE_PARALLEL_DML` Hint 禁用并行 DML。

### 在会话中启用和关闭并行 DML

默认情况下，即使 SQL语句中使用了 `PARALLEL` Hint，并行 DML 也是不开启的，所以还需要通过在会话上开启并行 DML。

在会话中启用并行 DML，MySQL 模式下语法为：

```sql
SET _FORCE_PARALLEL_DML_DOP = n;

```

其中 n 大于 1。

在会话中启用并行 DML，Oracle 模式下语法为：

```sql
ALTER SESSION ENABLE PARALLEL DML;

```

Oracle 模式下，如果需要在会话中强制开启并行 DML，请运行以下 SQL 语句：

```sql
ALTER SESSION FORCE PARALLEL DML PARALLEL n;

```

如下示例为，Oracle 模式下在会话中强制开启并行 DML。

```sql
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 时，一般使用由 Hint 指定的并行度来执行查询，其优先级高于会话中强制指定的并行度。Oracle 模式下的示例如下：

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 语句为：

```sql
SET _FORCE_PARALLEL_DML_DOP = 1;

```

如果要禁用并行 DML，Oracle 模式下的 SQL 语句为：

```sql
ALTER SESSION DISABLE PARALLEL DML;

```

禁用并行 DML 时，即使在 SQL 语句中使用 `PARALLEL` Hint，也不会执行并行 DML。 在会话中启用并行 DML 时，则并行执行对此会话中的所有 DML 语句都会生效。如果 SQL 语句通过 `ENABLE_PARALLEL_DML` Hint 启用并行 DML 时，则并行执行仅对指定语句生效。但是，如果没有具有并行属性的表，或者违反了并行操作的限制，即使启用了并行 DML，DML 操作仍然会串行执行。

### 支持分区间并行处理

将根据以下 SQL 语句进行展示说明表分区并行处理功能。

1. 创建测试表 `branch_sp_tbl_src`。

   ```sql
   CREATE TABLE branch_sp_tbl_src(id INT PRIMARY KEY, v INT) PARTITION BY KEY(id) PARTITIONS 4;

   ```
 2. 创建测试表 `branch_sp_tbl_dest`。

   ```sql
   CREATE TABLE branch_sp_tbl_dest LIKE branch_sp_tbl_src;

   ```
 3. 查看执行计划。

   执行下面 SQL 语句，将展示如何执行该插入操作。

   ```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;

   ```

   返回结果如下：

   ```shell
   +-------------------------------------------------------------------------------------------------------------------------------------+
   | 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 SELECT`
 - `UPDATE`
 - `DELETE`

如果表上存在下列索引类型，则需要支持并行执行：

- 局部索引
 - 单分区全局索引
 - 多分区全局索引

### 并行 DML 支持的场景

| 场景 | INSERT | UPDATE | DELETE | MERGE INTO |
| --- | --- | --- | --- | --- |
| 表上存在外键/TRIGGER（触发器）/PL UDF（用户自定义函数）/唯一索引   #### 说明    不能明确是否会触发触发器：当触发器与 DML 操作无关时，不会触发触发器时，会继续走并行 DML。例如，在 `UPDATE` 触发器的表上执行 `INSERT`、`DELETE` 等操作走并行 DML，执行 `UPDATE` 操作无法走并行 DML。 | 不支持 | 不支持 | 不支持 | 不支持 |
| 多表 DML | `INSERT ALL` 语法，不支持并行 DML | 支持关联 `UPDATE` 并行。 | 同 UPDATE | 语法不支持 |
| 自增列 | 部分场景支持并行 DML，详细介绍可参见下文示例。 | 支持 | 支持 | 支持（seq） |
| ArrayBinding batch 优化 | 不支持 | 不支持 | 不支持 | 不支持 |
| 使用 USER_VARIABLE | 不支持 | 不支持 | 不支持 | 不支持 |
| IGNORE | 不支持 | 不支持 | 不支持 | 语法不支持 |
| DBLink | 不支持 | 不支持 | 不支持 | 不支持 |
| 其他 | 以下语句不支持并行 DML：   - `INSERT ... VALUES ...` - `INSERT INTO ON DUPLICATE KEY UPDATE` - `REPLACE` | 包含 `ON UPDATE CURRENT_TIMESTAMP` 列不支持 |  | |

`INSERT` 的写入表有自增列时，假如自增列是主键或者分区键时，并且该列被指定时不支持并行 DML。

**示例如下：**

1. 创建表 `tbl1`，其中列 `col1` 为自增列并指定为主键。

   ```sql
   CREATE TABLE tbl1(col1 INT AUTO_INCREMENT PRIMARY KEY, col2 INT, col3 INT);

   ```
 2. 创建表 `tbl2`，其中列 `col1` 为自增列。

   ```sql
   CREATE TABLE tbl1(col1 INT AUTO_INCREMENT, col2 INT, col3 INT);

   ```
 3. 创建表 `tbl3`。

   ```sql
   CREATE TABLE tbl3(col1 INT, col2 INT, col3 INT);

   ```
 4. `INSERT` 的写入数据时，因为自增列 `col1` 是主键，所以不支持并行 DML。

   ```sql
   INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl1 SELECT * FROM tbl3;

   ```
 5. `INSERT` 的写入数据时，因为未指定列 `col1`，所以支持并行 DML。

   ```sql
   INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl1(col2, col3) SELECT col2, col3 FROM tbl3;

   ```
 6. `INSERT` 的写入数据时，因为列 `col1` 只是自增列，所以支持并行 DML。

   ```sql
   INSERT /*+enable_parallel_dml parallel(3)*/ INTO tbl2 SELECT * FROM tbl3;

   ```

### 并行 DML 关联更新

关联更新指在 `UPDATE` 语句中使用多表连接操作，基于关联表的数据更新目标表。通过并行 DML 功能，可以显著提升大规模数据更新操作的性能。

**示例如下：**

以下步骤展示了如何使用并行 DML 实现关联更新功能。

1. 创建两个测试表 `customers` 和 `orders`。

   ```sql
   CREATE TABLE customers (
       id INT PRIMARY KEY,
       name VARCHAR(50),
       customer_level VARCHAR(10)
   );

   ```

   ```sql
   CREATE TABLE orders (
       order_id INT PRIMARY KEY,
       customer_id INT,
       discount DECIMAL(3, 2),
       amount DECIMAL(10, 2)
   );

   ```
 2. 插入测试数据。

      - 向 `customers` 表插入客户数据：

       ```sql
       INSERT INTO customers (id, name, customer_level) VALUES
       (1, 'Alice', 'VIP'),
       (2, 'Bob', 'NORMAL'),
       (3, 'Charlie', 'OTHER');

       ```
      - 向 `orders` 表插入订单数据：

       ```sql
       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);

       ```
 3. 并行 DML 关联更新。

   通过 `EXPLAIN` 查看并行 DML 关联更新的执行计划：通过 `PARALLEL` Hint 指定并行度为 4，根据 `customers` 表中的客户等级（`customer_level`），更新 `orders` 表中的折扣字段（`discount`）。

   ```sql
   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;

   ```

   返回结果如下：

   ```shell
   +------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | 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

   ```

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
