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


MySQL 8.4 Reference Manual  /  ...  /  INSERT ... ON DUPLICATE KEY UPDATE Statement

15.2.7.2 插入...关于重复键更新语句

如果您指定了ON DUPLICATE KEY UPDATE子句,并且要插入的行将导致在UNIQUE索引或PRIMARY KEY中出现重复值,则会发生UPDATE操作。例如,如果列a被声明为UNIQUE,并且包含值1,以下两个语句具有相似的效果:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE t1 SET c=c+1 WHERE a=1;

这些效果不完全相同:对于InnoDB表,其中a是一个自增列,INSERT语句将增加自增值,但UPDATE语句不会。

如果列b也唯一,则INSERT语句等同于这个UPDATE语句:

UPDATE t1 SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;

如果a=1 OR b=2匹配多行,只更新一行。一般来说,您应该尽量避免在具有多个唯一索引的表上使用ON DUPLICATE KEY UPDATE子句。

使用 ON DUPLICATE KEY UPDATE,每行受影响的行数是1,如果该行插入为新行,2如果存在行被更新,0如果存在行保持当前值。如果在连接到mysqld时指定CLIENT_FOUND_ROWS标志到mysql_real_connect()C API函数,存在行保持当前值时受影响的行数为1(而不是0)。

如果表包含AUTO_INCREMENT列,并且INSERT ... ON DUPLICATE KEY UPDATE插入或更新行,LAST_INSERT_ID()函数返回AUTO_INCREMENT值。

ON DUPLICATE KEY UPDATE子句可以包含多个列赋值,使用逗号分隔。

ON DUPLICATE KEY UPDATE子句中的赋值表达式中,您可以使用VALUES(col_name)函数来引用来自INSERT部分的列值。在其他字面上,VALUES(col_name)ON DUPLICATE KEY UPDATE子句中引用的是将被插入的col_name值的值,如果没有重复键冲突发生。这一函数在多行插入中特别有用。VALUES()函数仅在ON DUPLICATE KEY UPDATE子句或INSERT语句中有效,否则返回<code class="literal">NULL</code>。示例:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
  ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

该语句与以下两个语句相同:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=3;
INSERT INTO t1 (a,b,c) VALUES (4,5,6)
  ON DUPLICATE KEY UPDATE c=9;
Note

使用VALUES()来引用新行和列是弃用的,且将在 MySQL 的未来版本中删除。相反,请使用行和列别名,如下几段本节中的描述所述。

可以使用行别名,带有可选的列别名,以便在VALUESSET子句中插入一行或多行数据。使用行别名new,可以将之前使用VALUES()访问新列值的语句写成以下形式:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

如果您还使用了列别名mnp,可以在赋值子句中省略行别名,并将语句写成以下形式:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

使用列别名时,您仍然需要在VALUES子句后面跟随行别名,即使您不直接使用它在赋值子句中。

使用VALUES()UPDATE子句中,像这样的一条INSERT ... SELECT ... ON DUPLICATE KEY UPDATE语句将抛出警告:

INSERT INTO t1
  SELECT c, c+d FROM t2
  ON DUPLICATE KEY UPDATE b = VALUES(b);

可以通过使用子查询来消除这些警告,如下所示:

INSERT INTO t1
  SELECT * FROM (SELECT c, c+d AS e FROM t2) AS dt
  ON DUPLICATE KEY UPDATE b = e;

您也可以使用行和列别名与SET子句一起使用,正如之前提到的那样。将SET用于前面显示的两条INSERT ... ON DUPLICATE KEY UPDATE语句,可以像下面这样做:

INSERT INTO t1 SET a=1,b=2,c=3 AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

INSERT INTO t1 SET a=1,b=2,c=3 AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

行别名不能与表名相同。如果不使用列别名,或者如果它们与列名相同,他们必须在ON DUPLICATE KEY UPDATE子句中使用行别名以进行区分。同一行别名下的列别名必须唯一(即,不允许有多个列别名引用同一行的列)。

对于INSERT ... SELECT语句,这些规则适用于在ON DUPLICATE KEY UPDATE子句中可以引用的SELECT查询表达式的形式:

  • 单个表的查询结果中的列引用,可以是派生表。

  • 多个表的连接查询结果中的列引用。

  • DISTINCT查询结果中的列引用。

  • 其他表中的列引用,只要SELECT语句不使用GROUP BY。一个副作用是,您必须对非唯一列名进行限定引用。

UNION查询结果中的列引用不受支持。要绕过这个限制,可以将UNION重写为派生表,以便其行可以被视为单个结果集。例如,这个语句会产生错误:

INSERT INTO t1 (a, b)
  SELECT c, d FROM t2
  UNION
  SELECT e, f FROM t3
ON DUPLICATE KEY UPDATE b = b + c;

相反,使用等效的语句将UNION重写为派生表:

INSERT INTO t1 (a, b)
SELECT * FROM
  (SELECT c, d FROM t2
   UNION
   SELECT e, f FROM t3) AS dt
ON DUPLICATE KEY UPDATE b = b + c;

将查询重写为派生表的技术也使得对GROUP BY查询结果中的列引用成为可能。

由于INSERT ... SELECT 语句的结果依赖于SELECT 中行的顺序,而这顺序不能总是被保证,因此在记录INSERT ... SELECT ON DUPLICATE KEY UPDATE 语句的源服务器和副本可能会分歧。因此,INSERT ... SELECT ON DUPLICATE KEY UPDATE 语句被标记为不安全的语句-基于复制。这些语句在使用语句-基于模式时会在错误日志中产生警告,并且在使用 MIXED 模式时将其写入到二进制日志中使用行-基于格式。对一个具有多个唯一或主键的表进行INSERT ... ON DUPLICATE KEY UPDATE 语句也被标记为不安全。 (Bug #11765650, Bug #58637)

请参见第19.2.1.1节,“Statement-Based and Row-Based Replication 的优缺”