防止MySQL重复插入时自动增加

时间:2011-05-07 23:49:13

标签: mysql insert duplicates innodb auto-increment

使用MySQL 5.1.49,我正在尝试实现标记系统 我遇到的问题是包含两列的表:id(autoincrement)tag(unique varchar) (InnoDB)

使用查询INSERT IGNORE INTO tablename SET tag="whatever"时,即使忽略插入,自动增量id值也会增加。

通常情况下这不会有问题,但我希望很多可能尝试为这个特定的表插入重复项,这意味着新行的id字段的下一个值将跳得太多

例如,我最终会得到一张表示3行但不好id的表

1   | test
8   | testtext
678 | testtextt

另外,如果我不执行INSERT IGNORE并且只执行常规INSERT INTO并处理错误,则自动增量字段仍会增加,因此下一个真正的插入仍然是错误的自动增量。

如果有INSERT重复的行尝试,有没有办法停止自动增量?

正如我对MySQL 4.1所理解的那样,这个值不会增加,但我要做的最后一件事是要么提前做很多SELECT语句来检查标签是否存在,或者更糟糕的是,降级我的MySQL版本。

9 个答案:

答案 0 :(得分:22)

您可以将INSERT修改为:

INSERT INTO tablename (tag)
SELECT $tag
FROM tablename
WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
LIMIT 1

其中$tag是您要添加的标记(正确引用或作为占位符)(如果尚未添加)。如果标签已经存在,这种方法甚至不会触发INSERT(以及随后的自动增量浪费)。你可能会提出比这更好的SQL,但上面应该可以解决这个问题。

如果你的表被正确编入索引,那么存在检查的额外SELECT将很快,数据库将不得不执行该检查。

但这种方法不适用于第一个标签。您可以使用您认为最终将被使用的标记为您的标记表设定种子,或者您可以对空表进行单独检查。

答案 1 :(得分:14)

v 5.5的MySQL文档说:

"If you use INSERT IGNORE and the row is ignored, the AUTO_INCREMENT counter 
is **not** incremented and LAST_INSERT_ID() returns 0, 
which reflects that no row was inserted."

参考:http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id

从版本5.1开始,InnoDB具有可配置的自动增量锁定功能。另见http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html#innodb-auto-inc ...

解决方法:使用选项innodb_autoinc_lock_mode = 0(传统)。

答案 2 :(得分:11)

我刚发现这个宝石......

http://www.timrosenblatt.com/blog/2008/03/21/insert-where-not-exists/

INSERT INTO [table name] SELECT '[value1]', '[value2]' FROM DUAL
WHERE NOT EXISTS(
    SELECT [column1] FROM [same table name]
    WHERE [column1]='[value1]'
    AND [column2]='[value2]' LIMIT 1
)

如果affectedRows = 1则插入;否则,如果affectedRows = 0则有重复。

答案 3 :(得分:10)

我发现mu太短了,答案很有帮助,但限制因为它不会在空表上插入。我找到了一个简单的修改方法:

INSERT INTO tablename (tag)
SELECT $tag
FROM (select 1) as a     #this line is different from the other answer
WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
LIMIT 1

使用“伪”表(select 1) as a替换from子句中的表允许该部分返回允许插入发生的记录。我正在运行mysql 5.5.37。非常感谢mu让我在那里大部分时间......

答案 4 :(得分:3)

您可以随时添加ON DUPLICATE KEY UPDATE Read here(不完全相同,但似乎可以解决您的问题)。

来自@ravi的评论

  

增量是否发生取决于   innodb_autoinc_lock_mode设置。如果设置为非零值,则   即使ON DUPLICATE KEY触发

,auto-inc计数器也会递增

答案 5 :(得分:1)

我有同样的问题,但不想使用innodb_autoinc_lock_mode = 0,因为我觉得我用榴弹炮杀死了一只苍蝇。

要解决此问题,我最终使用临时表。

create temporary table mytable_temp like mytable;

然后我插入了值:

insert into mytable_temp values (null,'valA'),(null,'valB'),(null,'valC');

之后,您只需执行另一次插入,但使用“not in”忽略重复项。

insert into mytable (myRow) select mytable_temp.myRow from mytable_temp 
where mytable_temp.myRow not in (select myRow from mytable);

我没有对性能进行测试,但是它完成了工作,并且易于阅读。虽然这很重要,因为我正在处理不断更新的数据,所以我不能忽视这些差距。

答案 6 :(得分:1)

接受的答案很有用,但是我在使用它时遇到了问题,基本上如果你的表没有条目,它就不会工作,因为选择使用了给定的表,所以相反我想出了以下内容,即使表是空白也插入,它也只需要你将表插入2个位置并将插入变量放在1个地方,少发生错误。

INSERT INTO database_name.table_name (a,b,c,d)
SELECT 
    i.*
FROM
    (SELECT 
        $a AS a, 
            $b AS b,
            $c AS c,
            $d AS d
            /*variables (properly escaped) to insert*/
    ) i
        LEFT JOIN        
    database_name.table_name o ON i.a = o.a AND i.b = o.b /*condition to not insert for*/
WHERE
    o.a IS NULL
LIMIT 1 /*Not needed as can only ever be one, just being sure*/

希望你觉得它很有用

答案 7 :(得分:0)

我在插入/更新查询后添加了一条额外的语句: ALTER TABLE table_name AUTO_INCREMENT = 1 然后他会自动获取最高的原始密钥ID加1。

答案 8 :(得分:0)

修改了 mu 的答案太短,(只需删除一行) 因为我是新手,我无法在他的回答下方发表评论。直接发到这里

下面的查询适用于第一个标签

 INSERT INTO tablename (tag)
 SELECT $tag
 WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)