Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
防止MySQL重复插入上的自动增量_Mysql_Insert_Duplicates_Innodb_Auto Increment - Fatal编程技术网

防止MySQL重复插入上的自动增量

防止MySQL重复插入上的自动增量,mysql,insert,duplicates,innodb,auto-increment,Mysql,Insert,Duplicates,Innodb,Auto Increment,使用MySQL 5.1.49,我试图实现一个标签系统 我遇到的问题是一个有两列的表:idautoincrement、tagunique varchar InnoDB 使用查询时,INSERT IGNORE INTO tablename SET tag=无论什么,即使忽略了INSERT,自动增量id值也会增加 通常这不会是一个问题,但我希望有很多可能的尝试为这个特定的表插入重复项,这意味着我的下一个新行id字段值将跳得太多 例如,我将得到一个包含3行但id不正确的表 另外,如果我不执行INSERT

使用MySQL 5.1.49,我试图实现一个标签系统 我遇到的问题是一个有两列的表:idautoincrement、tagunique varchar InnoDB

使用查询时,INSERT IGNORE INTO tablename SET tag=无论什么,即使忽略了INSERT,自动增量id值也会增加

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

例如,我将得到一个包含3行但id不正确的表

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

如果尝试插入重复行,是否有方法停止自动增量

我对MySQL 4.1的理解是,这个值不会增加,但我最不想做的就是提前做很多SELECT语句来检查标记是否存在,或者更糟糕的是,降级我的MySQL版本。

您可以随时添加重复密钥更新,但这似乎解决了您的问题

来自@ravi的评论


增量是否发生取决于 innodb_autoinc_锁定模式设置。如果设置为非零值,则 即使ON重复键触发,auto inc计数器也会增加


您可以将插入内容修改为以下内容:

INSERT INTO tablename (tag)
SELECT $tag
FROM tablename
WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
LIMIT 1
其中,$tag是正确引用的标记,或者是您想要添加的占位符(如果还没有)。如果标签已经存在,这种方法甚至不会触发插入和随后的自动增量损耗。您可能会想出比这更好的SQL,但上面的内容应该可以做到这一点

如果您的表被正确地索引,那么存在性检查的额外选择将很快,数据库无论如何都必须执行该检查


但是,这种方法对于第一个标记不起作用。你可以用一个你认为永远都会被使用的标签作为标记表的种子,或者你可以单独检查一个空表。

我刚刚发现了这个宝石


如果affectedRows=1,则将其插入;否则,如果affectedRows=0,则会出现重复。

我也有同样的问题,但不想使用innodb\u autoinc\u lock\u 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);

我还没有测试过它的性能,但它确实可以,而且很容易阅读。当然,这一点很重要,因为我处理的数据不断更新,所以我不能忽略这些差距。

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."
参考:

由于版本5.1,InnoDB具有可配置的自动增量锁定。另见


解决方法:使用选项innodb_autoinc_lock_mode=0 traditional。

我发现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

将from子句中的表替换为伪表select 1,以允许该部分返回允许插入的记录。我正在运行mysql 5.5.37。感谢mu让我在大部分时间里都能找到答案……

接受的答案很有用,但是我在使用它时遇到了一个问题,即如果您的表没有条目,它将无法工作,因为select正在使用给定的表,因此我提出了以下建议,即使该表为空,它也会插入,它还只需要在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*/

希望您觉得它有用

我只是在insert/update查询之后加了一条语句: 更改表格名称自动增量=1
然后他自动选择最高的prim密钥id加1。

修改mu的答案太短,只需删除一行即可 因为我是新手,我不能在他的回答下面发表评论。把它贴在这里

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

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

你在使用InnoDB吗?如果是的话,请看啊,是的。我只是做了一个快速编辑。我将查看链接TNX,只是该链接的后续内容。遗憾的是,它并没有真正解决问题,而是尝试对id字段使用bigint,以避免表爆炸。谢谢,我本来想避免的,但我有个主意。在每次插入后执行以下sql语句。它将id的自动增量重置为实际使用的最后一个。ALTER TABLE tags AUTO_INCREMENT=1可能存在此问题。它似乎会导致对所有行进行更新,但不会更改ID。它返回受影响的行xx,该行是表中的记录数。除此之外,我可以看到有一个虚拟表存储标记的数量
当添加新标签时,计数器将被更新。因此,我将不再使用自动增量,而是管理自己的ID。@罗伯特:如果您试图管理自己的ID,您将遇到并发问题。或者,您必须执行一系列表锁定来模拟对自动递增本身的受限访问。为什么在插入之前要避免检查重复项?你有没有检查过有没有真正的表演命中?没有,我想没什么太严重的,只是看起来像是坏习惯。说到这里,ALTER TABLE tags AUTO_INCREMENT=1的执行时间更糟糕。很好的解决方案,在最初插入一行时遇到了一些麻烦。我发现,如果您从中选择的表是空的,则不会进行插入。@Jbrown:没错,空表可能会有问题。我认为最简单的方法是在创建表时手动将标记添加到表中,您知道这些标记会被使用。通常我只需添加唯一约束,盲目插入标记,捕获/忽略预期错误路径,而不必担心自动递增值。@robert-如果您在该表上有唯一键,并且尝试在重复时使用…插入。。。同一个唯一密钥,它不能递增,如果递增,要么我们彼此误解了,要么你的MySQL有错误?不。它确实会增加。如果以下语句有一个重复的---INSERT INTO tags tag_text VALUES cur_string ON duplicate KEY UPDATE tag_text=cur_string-----则下一个实际插入的非重复项将具有问题中描述的id。我认为这更像是mysql的一个特性,而不是一个bug。有点古怪though@robert-有趣的是,我经常使用这个,如果可以的话,你使用的是哪个版本的MySQL?MySQL 5.1.49,表是innodb。您可能正在使用mysql 4.1,我认为该版本的非递增自动增量实际上被认为是一个bug。O.增量是否发生取决于innodb_autoinc_lock_模式设置。如果设置为非零值,即使启用了ON DUPLICATE键,auto inc计数器也会递增。有趣的是,对于非传统的,我使用INSERT时,它仍然会递增。对于MySQL 5.5.41,如果使用INSERT IGNORE并忽略行,最后一个\u INSERT \u ID与当前值保持不变,或者如果连接尚未成功执行插入,则返回0;如果操作未成功执行,则返回0preformed@langla:不,不是。这是我的错:tag是列名,$tag是列值:-非常好,谢谢。我一时想不出为什么我的代码不能处理空表。很好。基本上与Landon的解决方案相同,但使用双假表更优雅。这应该是可以接受的答案,因为即使表是空的,它也可以工作。非常感谢。
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*/
 INSERT INTO tablename (tag)
 SELECT $tag
 WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)