Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

15.2.12 REPLACE语句

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 工作方式与 INSERT 完全相同,除了如果旧行在表中具有相同的值作为新行的 PRIMARY KEYUNIQUE 索引,则删除旧行,然后插入新行。见 第 15.2.7 节,“INSERT 语句”

REPLACE 是 MySQL 对 SQL 标准的扩展。它要么插入,要么 删除 并插入。对于另一个 MySQL 对标准 SQL 的扩展——要么插入,要么 更新——见 第 15.2.7.2 节,“INSERT ... ON DUPLICATE KEY UPDATE语句”

DELAYED 插入和替换在 MySQL 5.6 中已弃用。在 MySQL 8.3 中,DELAYED 不再支持。服务器识别但忽略 DELAYED 关键字,将 replace 处理为非延迟 replace,并生成 ER_WARN_LEGACY_SYNTAX_CONVERTED 警告:REPLACE DELAYED 不再支持。语句已转换为 REPLACEDELAYED 关键字计划在未来版本中删除。

Note

REPLACE 只有在表具有 PRIMARY KEYUNIQUE 索引时才有意义。否则,它将等同于 INSERT,因为没有索引来确定新行是否重复。

所有列的值来自 REPLACE 语句中指定的值。任何缺失的列将设置为其默认值,就像 INSERT 一样。你不能引用当前行的值并将其用于新行。如果你使用赋值语句,如 SET col_name = col_name + 1,则右侧的列名引用将被视为 DEFAULT(col_name),因此赋值语句等同于 SET col_name = DEFAULT(col_name) + 1

你可以使用 VALUES ROW() 指定 REPLACE 尝试插入的列值。

要使用 REPLACE,你必须拥有表的 INSERTDELETE 权限。

如果生成的列被显式替换,则唯一允许的值是 DEFAULT。关于生成的列的信息,请参见 第 15.1.20.8 节,“CREATE TABLE 和生成的列”

REPLACE 支持使用 PARTITION 子句和逗号分隔的分区、子分区或两者的名称列表来选择分区。与 INSERT 一样,如果无法将新行插入到这些分区或子分区中,则 REPLACE 语句将失败,错误信息为 Found a row not matching the given partition set。更多信息和示例,请参见 第 26.5 节,“分区选择”

REPLACE语句返回一个计数,以指示受影响的行数。这是删除和插入行的总和。如果计数为 1,则表示插入了一行且没有删除任何行。如果计数大于 1,则表示在插入新行之前删除了一行或多行旧行。

受影响的行计数使得确定REPLACE是否仅添加了一行或是否也替换了一些行变得容易:检查计数是否为 1(添加)或大于 1(替换)。

如果您使用 C API,可以使用mysql_affected_rows()函数获取受影响的行计数。

您不能在同一个表中replace并从中选择子查询。

MySQL 使用以下算法来执行REPLACE(和LOAD DATA ... REPLACE):

  1. 尝试将新行插入表中

  2. 如果插入失败,因为主键或唯一索引的重复键错误:

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

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

在出现重复键错误的情况下,存储引擎可能会将REPLACE作为更新而不是删除加插入,但语义相同。没有用户可见的效果,除了可能的存储引擎如何增加Handler_xxx状态变量的差异。

因为SELECT语句的结果取决于行的顺序,而这个顺序不能总是保证,因此在记录这些语句时,源和副本可能会出现分歧。因此,REPLACE ... SELECT语句被标记为基于语句的复制不安全。这些语句在使用基于语句的模式时会在错误日志中生成警告,并在使用混合模式时以基于行的格式写入二进制日志。见第 19.2.1.1 节,“基于语句和基于行的复制的优缺点”

MySQL 8.3 支持TABLESELECTREPLACE,就像它与INSERT一样。见第 15.2.7.1 节,“INSERT ... SELECT 语句”,以获取更多信息和示例。

当修改现有的未分区表以适应分区,或者修改已分区表的分区时,您可能需要更改表的主键(见第 26.6.1 节,“分区键、主键和唯一键”)。您应该注意,如果您这样做了,REPLACE语句的结果可能会受到影响,就像修改非分区表的主键一样。考虑以下CREATE TABLE语句创建的表:

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

当我们在test2上运行与原始test表相同的两个REPLACE语句时,我们获得了不同的结果:

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)

这是因为,当在test2上运行时,idts列的值必须与现有行的值匹配,否则将插入一行。