Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


15.2.12 替换语句

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] ...

REPLACEINSERT的工作方式相同,但是如果表中的一个老行与新行的某个键值相同,老行将被删除,然后新行被插入。请参阅第15.2.7节,“INSERT Statement”

REPLACE 是 MySQL 扩展的 SQL 标准。它可以插入、删除和插入。另一个 MySQL 扩展到标准 SQL—that 可以插入或更新—请参阅第15.2.7.2节,“INSERT ... ON DUPLICATE KEY UPDATE Statement”

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

Note

REPLACE 只有在表中有主键或唯一索引时才有意义。否则,它将等同于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()函数获取。

不能在同一个子查询中替换表并从该表中选择。

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

  1. 尝试将新行插入表中

  2. 由于插入失败,因为出现了primary key或唯一索引的重复键错误:

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

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

可能情况是,在重复键错误情况下,存储引擎可能会将REPLACE作为更新,而不是删除加插入,但语义相同。没有可见的用户影响,可能会影响存储引擎中的Handler_xxx状态变量的递增。

由于REPLACE ... SELECT语句的结果取决于SELECT语句中的行顺序,这种顺序不能总是被保证,因此在日志记录这些语句时,源和副本可能会分歧。因此,REPLACE ... SELECT语句被标记为不安全的语句,以便在语句模式下在错误日志中显示警告,并在使用混合模式时使用行基于的格式将其写入二进制日志。请参见第19.2.1.1节,“Statement-Based and Row-Based Replication的优点和缺点”

MySQL 8.4支持TABLESELECT语句中的REPLACE,与INSERT语句相同。请参见第15.2.7.1节,“INSERT ... SELECT Statement”,了解更多信息和示例。

当修改一个未分区的表以适应分区时,或者修改一个已经分区的表的分区时,您可能需要更改表的主键(请参见第26.6.1节,“Partitioning Keys, Primary Keys, and Unique Keys”)。您应该注意,如果您这样做,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)
);

当我们在CREATE TABLE语句中使用INSERT语句时,我们获得不同的结果:

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)

这是因为,在CREATE TABLE语句中使用INSERT语句时,idts列的值必须与现有行的值匹配,以便将行替换;否则,将插入一行。