Answers:
我建议使用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
可以满足相同需求(甚至更多)的MERGE
语句,但是MySQL不支持该语句。
一位用户尝试编辑此帖子(主持人拒绝了该编辑)。修改尝试添加一个声明,该声明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 |
+----+------+
INSERT ... ON DUPLICATE KEY UPDATE ...
语句填充的表。许多数据是重复的,这导致AI PK的一个实例在两行之间从17,029,941增加到46,271,740。每次产生新的AI意味着您的范围可以很快被填满,您需要清理。这个桌子只有两个星期大了!
如果您想了解所有这些内容,以下是所有内容的逐项记录:
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
所以你有它。由于这些都是在几乎没有数据且不在生产中的新鲜表上执行的,因此执行时间是微观的且无关紧要的。任何拥有真实数据的人都将非常乐意为您提供数据。
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
。
需要添加的重要信息:使用INSERT IGNORE并且确实存在键冲突时,MySQL不会发出警告!
例如,如果您尝试一次插入100条记录,其中一条出现错误,则将进入交互模式:
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
如您所见:没有警告!在官方的Mysql文档中甚至错误地描述了此行为。
如果需要通知脚本,如果未添加某些记录(由于键冲突),则必须调用mysql_info()并将其解析为“ Duplicates”值。
mysqli_affected_rows()
了解INSERT
实际发生的情况。
Cannot add or update a child row: a foreign key constraint fails
,且行(即使有效的)添加。
INSERT IGNORE
,重复的键将被忽略,没有错误或警告。
我通常使用INSERT IGNORE
,这听起来也正是您要查找的行为。只要您知道将不会插入会导致索引冲突的行,并据此计划程序,就不会造成任何麻烦。
我知道这很旧,但是我会添加此注释,以防其他人(例如我)在尝试找到有关INSERT..IGNORE的信息时到达此页面。
如上所述,如果使用INSERT..IGNORE,则在执行INSERT语句时发生的错误将被视为警告。
没有明确提到的一件事是INSERT..IGNORE将导致无效值在插入时将调整为最接近的值(而无效值将导致查询中止,如果不使用IGNORE关键字)。
REPLACE
删除表中所有具有匹配的any PRIMARY
或UNIQUE
key的行,然后 删除INSERTs
。与IODKU相比,这可能需要做更多的工作。
如果要插入表中并且在主键或唯一索引冲突时,它将更新冲突的行,而不是插入该行。
句法:
insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;
现在在这里,此插入语句可能看起来与您之前看到的有所不同。该插入语句试图将table1中具有a和b值的行分别插入到column1和column2列中。
让我们深入了解以下语句:
例如:这里column1被定义为table1中的主键。
现在,如果在表1中,在列1中没有行具有值“ a”。因此,该语句将在table1中插入一行。
现在,如果在表1中,在列2中存在一行具有值“ a”的行。因此,此语句将使用“ c”更新行的column2值,其中column1值为“ a”。
因此,如果要插入新行,则在主键或唯一索引发生冲突时更新该行。
阅读更多有关此链接的信息
INSERT...ON DUPLICATE KEY UPDATE
最好防止意外的异常管理。
就我而言,我知道这一点,col1
并col2
可以创建唯一的复合索引。
它跟踪错误,但不会在重复项上引发异常。关于性能,以相同的值进行更新是有效的,因为MySQL注意到了这一点,并且不进行更新
INSERT INTO table
(col1, col2, col3, col4)
VALUES
(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1),
col2 = VALUES(col2)
使用这种方法的想法来自phpdelusions.net/pdo的评论。