在执行包含许多行的INSERT
语句时,我想跳过可能导致失败的重复条目。经过一些研究,我的选择似乎是使用:
ON DUPLICATE KEY UPDATE
这意味着需要花费一些不必要的更新,或者INSERT IGNORE
意味着邀请其他类型的未能在未事先通知的情况下失败。 我对这些假设是否正确?简单地跳过可能导致重复的行并继续执行其他行的最佳方法是什么?
答案 0 :(得分:957)
我建议使用INSERT...ON DUPLICATE KEY UPDATE
。
如果您使用INSERT IGNORE
,则如果导致重复键,则实际上不会插入该行。但该声明不会产生错误。它会生成警告。这些案例包括:
PRIMARY KEY
或UNIQUE
限制的列中插入重复键。 NOT NULL
约束的列中。如果您使用REPLACE
,MySQL实际上会在内部执行DELETE
后跟INSERT
,这会产生一些意想不到的副作用:
REPLACE
。DELETE
上的触发操作。 更正: REPLACE
和INSERT...ON DUPLICATE KEY UPDATE
都是针对MySQL的非标准专有发明。 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 |
+----+------+
答案 1 :(得分:166)
如果你想看看这一切意味着什么,这里是一切的吹嘘:
CREATE TABLE `users_partners` (
`uid` int(11) NOT NULL DEFAULT '0',
`pid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`uid`,`pid`),
KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
主键基于此快速参考表的两列。主键需要唯一值。
让我们开始吧:
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected
请注意,上面通过将列设置为等于自身来保存太多额外工作,实际上不需要更新
REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected
现在有一些多行测试:
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected
在控制台中没有生成其他消息,现在它在表数据中有4个值。除了(1,1)之外我删除了所有内容,所以我可以在相同的比赛场地进行测试
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected
REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected
所以你有它。由于这一切都是在一张几乎没有数据且没有生产的新桌子上进行的,因此执行的时间是微观的,无关紧要的。任何拥有真实数据的人都非常欢迎他们贡献它。
答案 2 :(得分:39)
要添加的重要内容:使用INSERT IGNORE并确实存在密钥违规时,MySQL不会发出警告!
如果您尝试一次插入100条记录,其中一条有错误,则会进入交互模式:
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
如你所见:没有警告!在官方的Mysql文档中甚至错误地描述了这种行为。
如果需要通知您的脚本,如果没有添加某些记录(由于密钥违规),您必须调用mysql_info()并解析它以获取“Duplicates”值。
答案 3 :(得分:18)
我经常使用INSERT IGNORE
,这听起来就像你正在寻找的那种行为。只要您知道不会插入会导致索引冲突的行并且您相应地规划您的程序,它就不会造成任何麻烦。
答案 4 :(得分:17)
我知道这已经过时了,但是我会添加这个注释,以防其他人(比如我)在尝试查找有关INSERT..IGNORE的信息时到达此页面。
如上所述,如果使用INSERT..IGNORE,则执行INSERT语句时发生的错误将被视为警告。
未明确提及的一件事是INSERT..IGNORE将导致无效值在插入时将被调整为最接近的值(而如果未使用IGNORE关键字,则无效值将导致查询中止)。
答案 5 :(得分:8)
答案 6 :(得分:8)
ON DUPLICATE KEY UPDATE在标准中不是真正的。它和REPLACE一样标准。请参阅SQL MERGE。
基本上这两个命令都是标准命令的替代语法版本。
答案 7 :(得分:3)
INSERT IGNORE的潜在危险。 如果您尝试更长时间地插入VARCHAR值,则列定义为 - 将截断并插入值,即使启用严格模式也是如此。
答案 8 :(得分:2)
如果在查询集的末尾使用insert ignore
语句,则会显示包含所有警告的表格,包括哪些ID是重复项。
答案 9 :(得分:2)
如果要插入表中以及主键或唯一索引的冲突,它将更新冲突的行而不是插入该行。
<强>语法:强>
insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;
现在,这个插入语句可能与您之前看到的不同。这个insert语句试图将table1中的一行和a和b的值分别插入到列column1和column2中。
让我们深入理解这个陈述:
例如:此处column1被定义为table1中的主键。
现在,如果在table1中,第1列中没有值为“a”的行。所以这个语句会在table1中插入一行。
现在,如果在table1中,在column2中有一行值为“a”。因此,此语句将使用“c”更新行的column2值,其中column1值为“a”。
因此,如果要插入新行,则在主键或唯一索引的冲突上更新该行 Read more on this link
答案 10 :(得分:1)
添加到此。如果您在同一条语句中同时使用 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';
答案 11 :(得分:0)
INSERT...ON DUPLICATE KEY UPDATE
来防止意外的异常管理。
就我而言,我知道col1
和col2
构成唯一的复合索引。
它跟踪错误,但不会在重复项上引发异常。 关于性能,与MySQL notices this and does not update it
相同值的更新是有效的INSERT INTO table
(col1, col2, col3, col4)
VALUES
(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1),
col2 = VALUES(col2)
使用这种方法的想法来自phpdelusions.net/pdo的评论。