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] ...
REPLACE
和INSERT
的工作方式相同,但是如果表中的一个老行与新行的某个键值相同,老行将被删除,然后新行被插入。请参阅第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 不再支持。语句被转换为 REPLACE。DELAYED
关键字将在未来的版本中被删除。
所有列的值来自REPLACE
语句指定的值。如果某个列缺失,会将其设置为该列的默认值,就像INSERT
一样。你不能引用当前行的值并将其用于新行。如果你使用一个赋值语句,如SET
,对右侧的列名将被视为col_name
= col_name
+ 1DEFAULT(
,因此赋值语句等同于col_name
)SET
。col_name
= DEFAULT(col_name
) + 1
你可以使用VALUES ROW()
指定REPLACE
尝试插入的列值。
要使用REPLACE
,您必须拥有该表的INSERT
和DELETE
权限。
如果生成列被显式替换,唯一允许的值是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
):
-
尝试将新行插入表中
-
由于插入失败,因为出现了primary key或唯一索引的重复键错误:
-
从表中删除冲突行,该行具有重复键值
-
尝试再次插入新行到表中
-
可能情况是,在重复键错误情况下,存储引擎可能会将REPLACE
作为更新,而不是删除加插入,但语义相同。没有可见的用户影响,可能会影响存储引擎中的Handler_
状态变量的递增。xxx
由于REPLACE ... SELECT
语句的结果取决于SELECT语句中的行顺序,这种顺序不能总是被保证,因此在日志记录这些语句时,源和副本可能会分歧。因此,REPLACE ... SELECT
语句被标记为不安全的语句,以便在语句模式下在错误日志中显示警告,并在使用混合模式时使用行基于的格式将其写入二进制日志。请参见第19.2.1.1节,“Statement-Based and Row-Based Replication的优点和缺点”。
MySQL 8.4支持TABLE
和SELECT
语句中的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
语句时,id
和ts
列的值必须与现有行的值匹配,以便将行替换;否则,将插入一行。