正确的方法来处理select + insert以避免重复错误

时间:2015-03-28 13:15:14

标签: mysql duplicates

您好我有一个简单的MySQL InnoDB表,只有两个字段:

  • id - autoincrement primary index
  • 名称 - 唯一索引

我正在并行地从各种来源导入一些数据,我需要确保数据在插入时没有重复,所以我正在执行以下操作:

SELECT `id` FROM `table` WHERE `name` = <name>;
 if `id` <= 0
  INSERT INTO `table` SET `name` = "<name>";
  return AUTO_INCREMENT
 else return `id`

这有效率为99.9999%但是它可能发生(并且发生在我身上)两个或多个不同的脚本正在插入相同的数据,因为SELECT返回id&lt; = 0所以两者都执行INSERT其中一个引起了错误。

我有两种可能的解决方案,但我不确定哪种方法最有效。

还有一条信息:最初导入将找不到表中的元素,但随着插入的元素越多,发现的概率就越大。经过一些粗略计算后,决赛桌将有大约7-10万条记录:

SELECT `id` FROM `table` WHERE `name` = <name>;
if `id` <= 0
 INSERT IGNORE INTO `table` SET `name` = "<name>";
 get AUTO_INCREMENT
  if AUTO_INCREMENT <=0
   SELECT `id` FROM `table` WHERE `name` = <name>;
   return `id`
  else return AUTO_INCREMENT
else return `id`

OR

INSERT IGNORE INTO `table` SET `name` = "<name>";
get AUTO_INCREMENT
 if AUTO_INCREMENT <=0
  SELECT `id` FROM `table` WHERE `name` = <name>;
  return `id`
 else return AUTO_INCREMENT

1 个答案:

答案 0 :(得分:1)

你正在遇到竞争条件。当您的代码检测到需要新插入时,您的两个客户端将竞相成为第一个插入该值的客户端。这是赢家通吃。您需要编写代码以避免此竞争条件。幸运的是,SQL是专门设计的,因此可以做到这一点。

这里有几个选择,都是针对MySQL的SQL方言。

一种是使用内置函数LAST_INSERT_ID()。我认为你的意思是get AUTO_INCREMENT

另一种是使用INSERT ... ON DUPLICATE KEY UPDATE

看起来你的逻辑意图做两件事:

  1. 确保name值在表中,如果尚未存在,请将其放在那里。
  2. 返回与名称值相关联的id值。
  3. 你可以这样做。

    INSERT IGNORE INTO `table` (name) VALUES (<name>);
    SELECT id FROM `table` WHERE name = <name>;
    

    请注意,INSERT IGNORE操作不会被命中数据库的不同程序之间的竞争条件所捕获,因为它是一个SQL语句。

    您可以使用LAST_INSERT_ID()来优化此项。

    INSERT IGNORE INTO `table` (name) VALUES (<name>);
    if (LAST_INSERT_ID()=0) then do the select.