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

当执行一个有很多行的INSERT语句时,我想跳过会导致失败的重复条目。 经过一番调查,我的select似乎是使用:

  • ON DUPLICATE KEY UPDATE ,这意味着一些成本不必要的更新,或
  • INSERT IGNORE这暗示了其他types的未能通知滑落的邀请。

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

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

如果使用INSERT IGNORE ,那么如果行导致重复键,则实际上不会插入该行。 但是声明不会产生错误。 它会生成一个警告。 这些情况包括:

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

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

  • 新的自动增量ID被分配。
  • 与外键相关的行可能被删除(如果使用级联外键)或者阻止REPLACE
  • DELETE上触发的触发器被不必要地执行。
  • 副作用也传播到复制从站。

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


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

参见下面的示例,用Percona Server 5.5.28进行testing。 configurationvariablesinnodb_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 | +----+------+ 

如果你想看看这一切意味着什么,这里是一切的一击:

 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 

现在有些多行testing:

 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)以外的所有东西,所以我可以从同一个游戏区域进行testing

 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 

所以你有它。 由于这一切都是在新鲜的餐桌上进行的,几乎没有数据,也没有在制作,所以执行的时间是微观和不相关的。 任何拥有真实世界数据的人都将不胜感激。

一些重要的补充:当使用INSERT IGNORE并且你有关键违规时,MySQL不会发出警告!

例如,如果您尝试一次插入100条logging(一个logging错误),您将进入交互模式:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

正如你所看到的:没有警告! 这种行为甚至在官方的Mysql文档中被错误地描述了。

如果您的脚本需要通知,如果有些logging还没有被添加(由于关键违规),您必须调用mysql_info()并parsing它的“重复”值。

我知道这是旧的,但我会添加这个笔记,以防其他人(像我)到达这个页面,而试图find信息INSERT..IGNORE。

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

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

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

在重复密钥更新是不是真的在标准。 这跟REPLACE是一样的标准。 请参阅SQL MERGE 。

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

Replace成看起来像一个选项。 或者你可以检查

 IF NOT EXISTS(QUERY) Then INSERT 

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

INSERT IGNORE的潜在危险。 如果您尝试插入VARCHAR值的时间过长,则使用 – 定义列的值将被截断并插入即使IF严格模式已启用。

如果使用insert ignore有一个SHOW WARNINGS; 在查询集结尾处的语句将显示包含所有警告的表,其中包括哪些ID是重复的。