### 13.2.9 替换语句

[](<>)

```
REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    { {VALUES | VALUE} (value_list) [, (value_list)] ...
      |
      VALUES row_constructor_list
    }

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    SET assignment_list

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    {SELECT ... | TABLE table_name}

value:
    {expr | DEFAULT}

value_list:
    value [, value] ...

row_constructor_list:
    ROW(value_list)[, ROW(value_list)][, ...]

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...
```

[`代替`](replace.html)工作原理完全一样[`插入`](insert.html), 除非表中的旧行与 a 的新行具有相同的值`首要的关键`或一个`独特`索引，在插入新行之前删除旧行。看[第 13.2.6 节，“INSERT 语句”](insert.html).

[`代替`](replace.html)是 SQL 标准的 MySQL 扩展。它要么插入，要么*删除*和插入。对于标准 SQL 的另一个 MySQL 扩展——插入或*更新*-看[第 13.2.6.2 节，“INSERT ... ON DUPLICATE KEY UPDATE 语句”](insert-on-duplicate.html).

`延迟`在 MySQL 5.6 中不推荐使用插入和替换。在 MySQL 8.0 中，`延迟`不支持。服务器识别但忽略`延迟`关键字，将替换作为非延迟替换处理，并生成一个`ER_WARN_LEGACY_SYNTAX_CONVERTED`警告：不再支持 REPLACE DELAYED。该语句已转换为 REPLACE。这`延迟`关键字计划在未来的版本中删除。发布。

笔记

[`代替`](replace.html)只有当一个表有一个`首要的关键`要么`独特`指数。否则，它变得等价于[`插入`](insert.html)，因为没有索引可用于确定新行是否与另一行重复。

所有列的值均取自[`代替`](replace.html)陈述。任何缺少的列都设置为其默认值，就像发生在[`插入`](insert.html).您不能引用当前行中的值并在新行中使用它们。如果您使用诸如`放 *`col_name`* = *`col_name`* + 1`，对右侧列名的引用被视为[`默认（*`col_name`*)`](miscellaneous-functions.html#function_default), 所以赋值等价于`放 *`col_name`* = 默认（*`col_name`*) + 1`.

在 MySQL 8.0.19 及更高版本中，您可以指定列值`代替`尝试使用插入[`值行（）`](values.html).

使用[`代替`](replace.html), 你必须同时拥有[`插入`](privileges-provided.html#priv_insert)和[`删除`](privileges-provided.html#priv_delete)表的特权。

[](<>)

如果生成的列被显式替换，则唯一允许的值是`默认`.有关生成的列的信息，请参阅[第 13.1.20.8 节，“创建表和生成的列”](create-table-generated-columns.html).

`代替`支持使用显式分区选择`划分`带有逗号分隔的分区、子分区或两者名称列表的子句。与[`插入`](insert.html)，如果无法将新行插入到任何这些分区或子分区中，则`代替`语句失败并出现错误发现与给定分区集不匹配的行。有关更多信息和示例，请参阅[第 24.5 节，“分区选择”](partitioning-selection.html).

这[`代替`](replace.html)语句返回一个计数以指示受影响的行数。这是删除和插入的行的总和。如果单行计数为 1[`代替`](replace.html)，插入了一行，没有删除任何行。如果计数大于 1，则在插入新行之前删除了一个或多个旧行。如果表包含多个唯一索引并且新行在不同唯一索引中重复了不同旧行的值，则单行可以替换多个旧行。

受影响的行数可以很容易地确定是否[`代替`](replace.html)仅添加一行或是否还替换了任何行：检查计数是 1（添加）还是更大（替换）。

如果您使用的是 C API，则可以使用以下方法获取受影响的行数[`mysql_affected_rows()`](https://dev.mysql.com/doc/c-api/8.0/en/mysql-affected-rows.html)功能。

您不能替换到表中并从子查询中的同一个表中进行选择。

MySQL 使用以下算法[`代替`](replace.html)（和[`加载数据...替换`](load-data.html)):

1.  尝试将新行插入表中

2.  由于主键或唯一索引发生重复键错误，插入失败：

    1.  从表中删除具有重复键值的冲突行

    2.  再次尝试将新行插入表中

    在发生重复键错误的情况下，存储引擎可能会执行`代替`作为更新而不是删除加插入，但语义是相同的。除了存储引擎增量的可能差异之外，没有用户可见的影响`处理程序_*`xxx`*`状态变量。

    因为结果`替换...选择`语句取决于行的顺序[`选择`](select.html)并且这个顺序不能总是得到保证，在记录源和副本的这些语句时可能会出现分歧。为此原因，`替换...选择`对于基于语句的复制，语句被标记为不安全。此类语句在使用 statement-based 模式时会在错误日志中产生警告，并在使用时使用 row-based 格式写入二进制日志`混合`模式。也可以看看[第 17.2.1.1 节，“基于语句和基于行的复制的优缺点”](replication-sbr-rbr.html).

    MySQL 8.0.19 及更高版本支持[`桌子`](table.html)也[`选择`](select.html)和`代替`, 就像它一样[`插入`](insert.html).看[第 13.2.6.1 节，“INSERT ... SELECT 语句”](insert-select.html)，了解更多信息和示例。

    在修改未分区的现有表以适应分区时，或者在修改已分区表的分区时，您可以考虑更改表的主键（请参阅[第 24.6.1 节，“分区键、主键和唯一键”](partitioning-limitations-partitioning-keys-unique-keys.html)）。你应该知道，如果你这样做，结果`代替`语句可能会受到影响，就像您修改非分区表的主键一样。考虑以下创建的表[`创建表`](create-table.html)陈述：


```
CREATE TABLE test (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
```

当我们创建这个表并运行mysql客户端显示的语句时，结果如下：

```
mysql> REPLACE INTO test VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.04 sec)

mysql> REPLACE INTO test VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 2 rows affected (0.04 sec)

mysql> SELECT * FROM test;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
1 row in set (0.00 sec)
```

现在我们创建第二个表几乎与第一个表相同，除了主键现在覆盖 2 列，如下所示（强调文本）：

```
CREATE TABLE test2 (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id, ts)
);
```

当我们继续运行`测试2`同样的两个`代替`就像我们在原版上所做的那样`测试`表，我们得到不同的结果：

```
mysql> REPLACE INTO test2 VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.05 sec)

mysql> REPLACE INTO test2 VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 1 row affected (0.06 sec)

mysql> SELECT * FROM test2;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | Old  | 2014-08-20 18:47:00 |
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
2 rows in set (0.00 sec)
```

这是因为，当运行时`测试2`， 这俩`id`和`ts`列值必须与要替换的行的现有行的值匹配；否则，插入一行。
