防止 MySQL 重复插入自动递增

发布于 2024-11-05 13:08:44 字数 678 浏览 0 评论 0原文

使用 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 并处理错误,自动增量字段仍然增加,因此下一个真正的插入仍然是错误的自动增量。

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

据我了解,对于 MySQL 4.1,这个值不会增加,但我想做的最后一件事是最终要么提前执行大量 SELECT 语句来检查标签是否存在,或者更糟,降级我的MySQL版本。

Using MySQL 5.1.49, I'm trying to implement a tagging system
the problem I have is with a table with two columns: id(autoincrement), tag(unique varchar) (InnoDB)

When using query, INSERT IGNORE INTO tablename SET tag="whatever", the auto increment id value increases even if the insert was ignored.

Normally this wouldn't be a problem, but I expect a lot of possible attempts to insert duplicates for this particular table which means that my next value for id field of a new row will be jumping way too much.

For example I'll end up with a table with say 3 rows but bad id's

1   | test
8   | testtext
678 | testtextt

Also, if I don't do INSERT IGNORE and just do regular INSERT INTO and handle the error, the auto increment field still increases so the next true insert is still a wrong auto increment.

Is there a way to stop auto increment if there's an INSERT duplicate row attempt?

As I understand for MySQL 4.1, this value wouldn't increment, but last thing I want to do is end up either doing a lot of SELECT statements in advance to check if the tags exist, or worse yet, downgrade my MySQL version.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

似最初 2024-11-12 13:08:44

您可以将 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 将会很快,并且数据库无论如何都必须执行该检查。

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

You could modify your INSERT to be something like this:

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

Where $tag is the tag (properly quoted or as a placeholder of course) that you want to add if it isn't already there. This approach won't even trigger an INSERT (and the subsequent autoincrement wastage) if the tag is already there. You could probably come up with nicer SQL than that but the above should do the trick.

If your table is properly indexed then the extra SELECT for the existence check will be fast and the database is going to have to perform that check anyway.

This approach won't work for the first tag though. You could seed your tag table with a tag that you think will always end up being used or you could do a separate check for an empty table.

渔村楼浪 2024-11-12 13:08:44

我刚刚找到了这个宝石...

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,则存在重复项。

I just found this gem...

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
)

If affectedRows = 1 then it inserted; otherwise if affectedRows = 0 there was a duplicate.

吻泪 2024-11-12 13:08:44

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."

Ref: 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 (传统)。

The MySQL documentation for v 5.5 says:

"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."

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

Since version 5.1 InnoDB has configurable Auto-Increment Locking. See also http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html#innodb-auto-inc...

Workaround: use option innodb_autoinc_lock_mode=0 (traditional).

悲念泪 2024-11-12 13:08:44

我发现 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)替换from子句中的表(select 1)作为允许该部分返回允许插入发生的记录。我正在运行 mysql 5.5.37。谢谢你带我去那里的大部分路......

I found mu is too short's answer helpful, but limiting because it doesn't do inserts on an empty table. I found a simple modification did the trick:

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

Replacing the table in the from clause with a "fake" table (select 1) as a allowed that part to return a record which allowed the insert to take place. I'm running mysql 5.5.37. Thanks mu for getting me most of the way there ....

两人的回忆 2024-11-12 13:08:44

接受的答案很有用,但是我在使用它时遇到了一个问题,基本上如果您的表没有条目,它将无法工作,因为选择正在使用给定的表,所以我想出了以下内容,即使表是空白的,也只需要在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*/

希望你觉得它有用

The accepted answer was useful, however I ran into a problem while using it that basically if your table had no entries it would not work as the select was using the given table, so instead I came up with the following, which will insert even if the table is blank, it also only needs you to insert the table in 2 places and the inserting variables in 1 place, less to get wrong.

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*/

Hope you find it useful

请持续率性 2024-11-12 13:08:44

您始终可以添加ON DUPLICATE KEY UPDATE

来自@ravi 的评论

增量是否发生取决于
innodb_autoinc_lock_mode 设置。如果设置为非零值,则
即使 ON DUPLICATE KEY 触发,自动增量计数器也会递增

You can always add ON DUPLICATE KEY UPDATE Read here (not exactly, but solves your problem it seems).

From the comments, by @ravi

Whether the increment occurs or not depends on the
innodb_autoinc_lock_mode setting. If set to a non-zero value, the
auto-inc counter will increment even if the ON DUPLICATE KEY fires

安稳善良 2024-11-12 13:08:44

我遇到了同样的问题,但不想使用 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);

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

I had the same problem but didn't want to use innodb_autoinc_lock_mode = 0 since it felt like I was killing a fly with a howitzer.

To resolve this problem I ended up using a temporary table.

create temporary table mytable_temp like mytable;

Then I inserted the values with:

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

After that you simply do another insert but use "not in" to ignore duplicates.

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

I haven't tested this for performance, but it does the job and is easy to read. Granted this was only important because I was working with data that was constantly being updated so I couldn't ignore the gaps.

别忘他 2024-11-12 13:08:44

修改了mu的答案太短,(只需删除一行)
由于我是新手,我无法在他的回答下发表评论。只需将其发布到此处,

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

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

modified the answer from mu is too short, (simply remove one line)
as i am newbie and i cannot make comment below his answer. Just post it here

the query below works for the first tag

 INSERT INTO tablename (tag)
 SELECT $tag
 WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
枉心 2024-11-12 13:08:44

我只是在插入/更新查询之后添加了一条额外的语句:
更改表表名 AUTO_INCRMENT = 1
然后他自动选取最高的prim key id加1。

I just put an extra statement after the insert/update query:
ALTER TABLE table_name AUTO_INCREMENT = 1
And then he automatically picks up the highest prim key id plus 1.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文