小编典典

“INSERT IGNORE”与“INSERT ... ON DUPLICATE KEY UPDATE”

all

在执行INSERT包含多行的语句时,我想跳过否则会导致失败的重复条目。经过一些研究,我的选择似乎是使用以下任一:

  • ON DUPLICATE KEY UPDATE这意味着以某种代价进行不必要的更新,或者
  • INSERT IGNORE暗示其他类型的失败会在未经通知的情况下溜进来。

我的这些假设是对的吗?简单地跳过可能导致重复的行并继续到其他行的最佳方法是什么?


阅读 138

收藏
2022-03-01

共1个答案

小编典典

我建议使用INSERT...ON DUPLICATE KEY UPDATE.

如果使用INSERT IGNORE,则如果它导致重复键,则实际上不会插入该行。但该语句不会产生错误。它会生成一个警告。这些案例包括:

  • PRIMARY KEY在具有orUNIQUE约束 的列中插入重复键。
  • 将 NULL 插入具有NOT NULL约束的列中。
  • 向分区表插入一行,但插入的值未映射到分区。

如果使用REPLACE,MySQL 实际上会在内部执行 aDELETE后跟 an INSERT,这会产生一些意想不到的副作用:

  • 分配了一个新的自增 ID。
  • 可能会删除具有外键的相关行(如果您使用级联外键),否则会阻止REPLACE.
  • 触发的触发器DELETE被不必要地执行。
  • 副作用也会传播到副本。

更正: 两者都是 MySQL 特有REPLACEINSERT...ON DUPLICATE KEY UPDATE非标准专有发明。ANSI
SQL 2003 定义了一个MERGE可以解决相同需求(甚至更多)的语句,但 MySQL 不支持该MERGE语句。


一位用户试图编辑这篇文章(编辑被版主拒绝)。该编辑尝试添加一个声明,该声明INSERT...ON DUPLICATE KEY UPDATE会导致分配一个新的自动增量 ID。确实 生成 了新的id ,但没有在更改的行中使用。

请参阅下面的演示,使用 Percona Server 5.5.28 进行测试。配置变量innodb_autoinc_lock_mode=1(默认):

mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   10 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

上面演示了 IODKU 语句检测到重复,并调用更新来更改u. 请注意,AUTO_INCREMENT=3表示生成了一个 id,但未在该行中使用。

REPLACE确实删除了原始行并插入了一个新行,生成 存储了一个新的自动增量 ID:

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+
2022-03-01