在MySQL中删除后自动递增


74

我有一个带有主键字段且具有AUTO_INCREMENT的MySQL表。在阅读了这里的其他文章后,我发现人们遇到同样的问题,答案也各不相同。有些人建议不要使用此功能,其他人则说它不能被“修复”。

我有:

table: course
fields: courseID, courseName

示例:表中的记录数:18.如果删除记录16、17和18-我希望输入的下一条记录的courseID为16,但是由于最后输入的courseID为18,所以它将为19。

我的SQL知识并不奇怪,但是无论如何,是否可以通过查询(或phpMyAdmin界面中的设置)刷新或更新此计数?

该表将与数据库中的其他表相关。


有了所有建议,我决定忽略这个“问题”。我将简单地删除和添加记录,同时让自动增量完成它的工作。我猜数字到底有多大无关紧要,因为它仅用作唯一标识符,并且没有(如上所述)业务含义。

对于那些可能对我的原始帖子感到困惑的人:我不想使用此字段来知道我有多少条记录。我只是希望数据库看起来整洁,并具有更多的一致性。


3
如果仅删除记录16,会发生什么?
约翰·博克

好点子!我应该提到。理想情况下,将所有内容都向上移动一个?
OmidTahouri'2

从某种意义上讲,我遇到了一个问题,与您的问题相反。换句话说,我有一张ENTITY桌子和一张ENTITY_LOG桌子。每次我在ENTITY表中插入,更新或删除某些内容时,都会将该活动ENTITY_LOG以及实体的记录到表中Id。最近,一个用户删除了表中的一堆条目ENTITY。并在日志表中进行了相应的日志条目。今天,当另一个用户尝试向表中添加新条目时ENTITY,生成的Id碰巧与先前删除的实体相同!我使用JdbcTemplate和InnoDB。
网络用户

@WebUser我不明白为什么会这样。我非常确定(并且会期望)MySQL会处理所有这些。不确定JdbcTemplate的一面-也许里面有东西在做(?)。祝好运!
OmidTahouri 2012年

@OmidTahouri,我完全同意。这当然是一个奇怪的问题。因此,我将尝试复制问题并逐步执行代码。感谢您的确认!
网络用户

Answers:


72

您尝试做的事情听起来很危险,因为这不是预期的用途AUTO_INCREMENT

如果您真的想找到最低的未使用密钥值,请不要使用AUTO_INCREMENT,并手动管理密钥。但是,这不是推荐的做法。

退后一步,问“为什么需要回收键值? ”未签名INT(或BIGINT)是否没有提供足够大的键空间?

18,446,744,073,709,551,615在应用程序的整个生命周期中,您是否真的会拥有比唯一记录更多的记录?


1
没错 最好忽略它。考虑到这仅是一项任务,我不会大量参加,而且寿命很短。
OmidTahouri

4
出于多种原因同意高度危险。一个,在多用户系统中,您每秒可能有成百上千的更新,尝试重写auto inc可能会减慢系统速度或损害系统。另外两个开发人员可能不知道您正在这样做,并通过id链接记录,从而破坏了系统。等
PurplePilot 2010年

好点。答案让我三思。我曾经在寻找auto_incr中没有任何差距。因为我打算以某种方式遍历id。但是我想最好是遍历行:)
Nils Sens

1
所以根本不可能吗?有充分的理由希望在测试环境中执行此操作。
迈克尔(Michael)

38
ALTER TABLE foo AUTO_INCREMENT=1

如果您删除了最近的条目,则应将其设置为使用下一个最低的条目。例如,只要还没有19,删除16-18就会将自动增量重置为使用16。


编辑:我错过了关于phpmyadmin的位。您也可以在此处进行设置。转到表格屏幕,然后单击“操作”选项卡。这里有一个AUTOINCREMENT场那里,你可以设置任何你需要手动。


1
OP可以做到这一点,但他不应该这样做。考虑到OP正在尝试执行的操作,您应该重新考虑此建议。
Mike Sherov

10
我不是在这里告诉他如何布置他的数据库或如何执行他的业务逻辑。他还在自己的帖子中提到,他在阅读其他帖子/页面时指出这是个坏主意,因此他知道这不是推荐的做法,但无论如何都会继续这样做。
10年

5
这是当前提供给问询者唯一明确问题的最直接答案。没有任何“建议”。
航空

1
始终建议您不要使用您的解决方案,如果您有反对的理由,OP还是建议您不要使用该解决方案。
ToBe 2014年

4
尽管这一个不好的做法,但这是最好的答案,因为它实际上回答了OP的要求
Jojodmo

16

数据库中的主要自动增量键用于唯一地标识给定的行,并且不应赋予任何业务含义。因此,保留主键不变,并添加另一个名为example的列courseOrder。然后,当您从数据库中删除一条记录时,您可能希望发送一条附加的UPDATE语句,以减少courseOrder具有courseOrder大于当前要删除的行的所有行的列。

附带说明一下,您永远不要修改关系数据库中主键的值,因为可能会有其他表将其引用为外键,并且修改它可能会违反引用约束。


他希望保持计数,而不是订购。UPDATE似乎不必要。
Mike Sherov

好吧,如果他希望保持计数,则无需添加其他列。简单的count聚合函数将完成这项工作。
Darin Dimitrov'2

好的谢谢。在这么短的时间内有很多答案/评论!:O我试图将它们全部考虑在内。我将研究count函数:)
OmidTahouri 2010年

13

尝试:

SET @num:= 0;

更新your_table SET id = @num:=(@ num + 1);

ALTER TABLE tableNameAUTO_INCREMENT = 1;

这将重置自动递增的值,然后在为新行创建新值时对每一行进行计数。

例如:之前

  • 1:此处的第一个值
  • 2:此处的第二个值
  • X:删除值
  • 4:桌子的其余部分
  • 5:其余的..

因此表格将显示数组:1、2、4、5

示例:之后(如果使用此命令,您将获得)

  • 1:此处的第一个值
  • 2:此处的第二个值
  • 3:桌子的其余部分
  • 4:其余的

没有删除值的痕迹,增量的其余部分继续使用此新计数。

  1. 如果代码中的某处使用了自动递增的值,则可能会导致此问题。
  2. 如果您在代码中未使用此值,则一切正常。

如果要保留旧值并将auto_increment设置为最大值,则需要找到最大值:1.通过对它们进行排序,并获取最后一行的值,2.然后将自动增量设置为该最大值+代码中的1。
Claod

真的很感激。它对我解决朋友的问题有很大帮助。在互联网上进行了大量搜索后,我找到了这个答案。
阿里伊斯兰教

4

您不应该依赖AUTO_INCREMENT ID来告诉您表中有多少条记录。您应该使用SELECT COUNT(*) FROM course。ID可以唯一标识课程,并且可以在其他表格中用作参考,因此您不应重复ID,也不应试图重置自动增量字段。


我认为他认为这是某种错误。MySQL已经20岁了。这绝对不是疏忽。有一个很好的理由说明自动增量功能不会回收密钥。你是完全正确的迈克。
查尔斯·罗伯逊

2

您可以像这样选择ID:

set @rank = 0;
select id, @rank:=@rank+1 from tbl order by id

结果是ID列表及其在序列中的位置。

您还可以像这样重置ID:

set @rank = 0;
update tbl a join (select id, @rank:=@rank+1 as rank from tbl order by id) b
  on a.id = b.id set a.id = b.rank;

您也可以像这样打印出第一个未使用的ID:

select min(id) as next_id from ((select a.id from (select 1 as id) a
  left join tbl b on a.id = b.id where b.id is null) union
  (select min(a.id) + 1 as id from tbl a left join tbl b on a.id+1 = b.id
  where b.id is null)) c;

每次插入后,您可以重置auto_increment:

alter table tbl auto_increment = 16

或在执行插入操作时显式设置id值:

insert into tbl values (16, 'something');

通常这不是必需的,您具有count(*)在结果集中创建排名数字的能力。典型的排名可能是:

set @rank = 0;
select a.name, a.amount, b.rank from cust a,
  (select amount, @rank:=@rank+1 as rank from cust order by amount desc) b
  where a.amount = b.amount

客户按消费金额排名。


2

我来这里是为了寻找标题问题的答案,"MySQL - Auto Increment after delete"但我只能在问题中找到答案

通过使用类似:

DELETE FROM table;
ALTER TABLE table AUTO_INCREMENT = 1;

请注意,Darin Dimitrov的回答确实很好地解释了AUTO_INCREMENT它的用法。在做可能会后悔的事情之前,先看看那里。

PS:问题本身更多,"Why you need to recycle key values?"Dolph的答案涵盖了这一点。


2

我可以想到很多情况下可能需要执行此操作的情况,尤其是在迁移或开发过程中。例如,我现在不得不通过交叉连接两个现有表(作为复杂设置过程的一部分)来创建一个新表,然后我需要在事件之后添加一个主键。您可以删除现有的主键列,然后执行此操作。

ALTER TABLE my_table ADD `ID` INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`ID`);

对于实时系统,这不是一个好主意,尤其是在存在其他带有外键指向它的表的情况下。


1

我有一个非常简单但棘手的方法。

删除行时,可以将ID保留到另一个临时表中。之后,当您将新数据插入主表时,您可以从临时表中搜索并选择ID。因此,请在此处进行检查。如果临时表没有ID,则将最大ID计算到主表中,并将新ID设置为:new_ID = old_max_ID+1

注意:您不能在此处使用自动递增功能。


1

您试图做的事非常危险。仔细考虑一下。自动递增的默认行为是有充分的理由的。

考虑一下:

一个表中的记录被删除,该表与另一表有关系。由于审核原因,无法删除第二个表中的相应记录。该记录从第一个表变为孤立的。如果在第一个表中插入了一条新记录,并且使用了顺序主键,则该记录现在链接到该孤儿。显然,这很糟糕。通过使用自动递增的PK,始终可以确保从未使用过的ID。这意味着孤儿仍然是孤儿,这是正确的。


1

实际上,有一种解决方法。首先,删除auto_incremented主键列,然后再次添加它,如下所示:

ALTER TABLE table_name DROP column_name;
ALTER TABLE table_name ADD column_name int not null auto_increment primary key first;

0

您可以使用mysql客户端软件/脚本来指定删除所需记录后主键应从何处开始。



0

它绝对不值得推荐。如果您的大型数据库有多个表,则可能已在表2中将一个用户ID保存为ID。如果重新排列表1,则预期的用户ID可能最终不会成为预期的表2 ID。


0

MYSQL查询自动增量解决方案。当您在软件测试阶段插入许多记录时,它可以完美工作。现在,您要向客户端实时启动应用程序,并且要从1开始自动递增。

为了避免任何不必要的问题,为了更安全,请首先导出.sql文件。
然后按照以下步骤操作:

  • 步骤1)首先创建现有表MySQL Command的副本以创建Copy:

    CREATE TABLE new_Table_Name  SELECT * FROM existing_Table_Name;
    

    将使用约束以外的所有行创建表的精确副本。
    它不会将自动增量和主键之类的约束复制到new_Table_name

  • 步骤2)删除所有行如果未在测试阶段插入数据,则该行将无用。如果数据很重要,则直接转到步骤3。

    DELETE from new_Table_Name;
    
  • 步骤3)要添加约束,请转到表的结构

    • 3A)从“更多”选项(如果需要)中添加主键约束。
    • 3B)从“更改”选项添加“自动增量”约束。对于此设置,定义的值为None
    • 3C)删除existing_Table_Name并
    • 3D)将new_Table_Name重命名为existing_Table_Name。

现在它将完美运行。新的第一条记录将在“自动增量”列中获得第一个值。


-1
if($id == 1){ // deleting first row
            mysqli_query($db,"UPDATE employees  SET id=id-1 WHERE id>1");
        }
        else if($id>1 && $id<$num){ // deleting middle row
            mysqli_query($db,"UPDATE employees  SET id=id-1 WHERE id>$id");
        }
        else if($id == $num){ // deleting last row
            mysqli_query($db,"ALTER TABLE employees AUTO_INCREMENT = $num");
        }
        else{
            echo "ERROR";
        }

        mysqli_query($db,"ALTER TABLE employees AUTO_INCREMENT = $num");

1
MySQL是一个关系数据库。当有多个表时,需要在表之间定义一个关系。这是使用ID列完成的。这种方法的问题在于,如果您开始对表的索引重新编号,将会对与其有关系的所有表产生影响。
liamvictor 17-3-17

-1

这是一个可以解决您的问题的功能

    public static void fixID(Connection conn, String table) {

    try {
        Statement myStmt = conn.createStatement();
        ResultSet myRs;
        int i = 1, id = 1, n = 0;
        boolean b;
        String sql;

        myRs = myStmt.executeQuery("select max(id) from " + table);
        if (myRs.next()) {
            n = myRs.getInt(1);
        }
        while (i <= n) {
            b = false;
            myRs = null;
            while (!b) {
                myRs = myStmt.executeQuery("select id from " + table + " where id=" + id);
                if (!myRs.next()) {
                    id++;
                } else {
                    b = true;
                }
            }

            sql = "UPDATE " + table + " set id =" + i + " WHERE id=" + id;
            myStmt.execute(sql);
            i++;
            id++;
        }

    } catch (SQLException e) {
        e.printStackTrace();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.