在执行包含许多行的INSERT语句时,我希望跳过重复的条目,否则会导致失败。经过一番研究,我的选择似乎是使用任何一种:
ON DUPLICATE KEY UPDATE,这意味着在某些代价下进行不必要的更新,或者 INSERT IGNORE暗示着邀请其他类型的失败在未通知的情况下潜入。
我的这些假设对吗?简单地跳过可能导致重复的行并继续到其他行,最好的方法是什么?
在执行包含许多行的INSERT语句时,我希望跳过重复的条目,否则会导致失败。经过一番研究,我的选择似乎是使用任何一种:
ON DUPLICATE KEY UPDATE,这意味着在某些代价下进行不必要的更新,或者 INSERT IGNORE暗示着邀请其他类型的失败在未通知的情况下潜入。
我的这些假设对吗?简单地跳过可能导致重复的行并继续到其他行,最好的方法是什么?
当前回答
除此之外。如果在同一个语句中同时使用INSERT IGNORE和ON DUPLICATE KEY UPDATE,如果插入发现重复的键,更新仍然会发生。换句话说,更新优先于忽略。但是,如果ON DUPLICATE KEY UPDATE子句本身导致重复键错误,则该错误将被忽略。
如果您有多个唯一键,或者您的更新试图违反外键约束,就会发生这种情况。
CREATE TABLE test
(id BIGINT (20) UNSIGNED AUTO_INCREMENT,
str VARCHAR(20),
PRIMARY KEY(id),
UNIQUE(str));
INSERT INTO test (str) VALUES('A'),('B');
/* duplicate key error caused not by the insert,
but by the update: */
INSERT INTO test (str) VALUES('B')
ON DUPLICATE KEY UPDATE str='A';
/* duplicate key error is suppressed */
INSERT IGNORE INTO test (str) VALUES('B')
ON DUPLICATE KEY UPDATE str='A';
其他回答
ON DUPLICATE KEY UPDATE并不是真正的标准。它和REPLACE一样标准。参见SQL MERGE。
实际上,这两个命令都是标准命令的替代语法版本。
插入……ON DUPLICATE KEY UPDATE优先用于防止意外异常管理。
当您只有**1个唯一约束时,此解决方案才有效
在我的例子中,我知道col1和col2组成了一个唯一的综合指数。
它会跟踪错误,但不会对副本抛出异常。 关于性能,相同值的更新是有效的,因为MySQL注意到这一点,并且不更新它
INSERT INTO table
(col1, col2, col3, col4)
VALUES
(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1),
col2 = VALUES(col2)
使用这种方法的想法来自phpdelusions.net/pdo上的评论。
替换成似乎是一个选择。或者你可以查一下
IF NOT EXISTS(QUERY) Then INSERT
这将插入或删除然后插入。我倾向于先进行IF NOT EXISTS检查。
如果使用插入忽略有一个显示警告;语句将显示一个包含所有警告的表,包括哪些id是重复的。
我推荐使用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 |
+----+------+