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

ON DUPLICATE KEY UPDATE,这意味着在某些代价下进行不必要的更新,或者 INSERT IGNORE暗示着邀请其他类型的失败在未通知的情况下潜入。

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


当前回答

如上所述,如果使用INSERT..IGNORE,执行INSERT语句时发生的错误将被视为警告。

有一件事没有明确提到的是INSERT..IGNORE会导致无效值在插入时被调整为最接近的值(而无效值会导致查询中止,如果没有使用IGNORE关键字)。

其他回答

替换成似乎是一个选择。或者你可以查一下

IF NOT EXISTS(QUERY) Then INSERT

这将插入或删除然后插入。我倾向于先进行IF NOT EXISTS检查。

我推荐使用INSERT…重复密钥更新。

如果使用INSERT IGNORE,那么如果它产生了重复的键,那么该行实际上不会被插入。但是该语句不会生成错误。相反,它生成一个警告。这些情况包括:

在具有PRIMARY key或UNIQUE约束的列中插入重复键。 在具有NOT NULL约束的列中插入NULL。 向分区表插入一行,但插入的值不映射到分区。

如果你使用REPLACE, MySQL实际上在内部执行了一个DELETE,然后是一个INSERT,这有一些意想不到的副作用:

分配一个新的自动递增ID。 带有外键的依赖行可能会被删除(如果使用级联外键),或者阻止REPLACE。 在DELETE上触发的触发器被不必要地执行。 副作用也会传播到副本。

修正:替换和插入…ON DUPLICATE KEY UPDATE是MySQL特有的非标准专利发明。ANSI SQL 2003定义了一个MERGE语句,可以解决同样的需求(以及更多),但MySQL不支持MERGE语句。


一个用户试图编辑这篇文章(编辑被版主拒绝了)。编辑试图添加一个声明,插入…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 |
+----+------+

我经常使用INSERT IGNORE,这听起来也正是您正在寻找的那种行为。只要知道不会插入会导致索引冲突的行,并且相应地规划程序,就不应该造成任何麻烦。

如果要在表中插入与之冲突的主键或唯一索引,它将更新冲突的行,而不是插入该行。

语法:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

现在在这里,这个插入语句看起来可能与您之前看到的不同。此插入语句试图将table1中值为a和b的行分别插入列columnn1和column2。

让我们深入理解这句话:

例如:这里的column1被定义为table1中的主键。

现在,如果在table1中,column n1中没有值为“a”的行。因此,这条语句将在table1中插入一行。

现在,如果在table1中,在column2中有一行值为“a”。因此,这条语句将用“c”更新行column2的值,其中columnn1的值为“a”。

因此,如果你想插入一个新行,否则更新该行冲突的主键或唯一索引。 在这个链接上阅读更多

ON DUPLICATE KEY UPDATE并不是真正的标准。它和REPLACE一样标准。参见SQL MERGE。

实际上,这两个命令都是标准命令的替代语法版本。