“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 KEY
或UNIQUE
约束的列中插入重复键。 - 将NULL插入到具有
NOT NULL
约束的列中。 - 将行插入到分区表中,但插入的值不映射到分区。
如果你使用REPLACE
,MySQL实际上会在内部做一个DELETE
然后是INSERT
,这有一些意想不到的副作用:
- 新的自动增量ID被分配。
- 与外键相关的行可能被删除(如果使用级联外键)或者阻止
REPLACE
。 - 在
DELETE
上触发的触发器被不必要地执行。 - 副作用也传播到复制从站。
更正: REPLACE
和INSERT...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是重复的。