MySQL中删除后自动增量
Posted
技术标签:
【中文标题】MySQL中删除后自动增量【英文标题】:Auto Increment after delete in MySQL 【发布时间】:2011-01-13 22:08:47 【问题描述】:我有一个 mysql 表,它的主键字段启用了 AUTO_INCREMENT。 在这里阅读其他帖子后,我注意到人们有同样的问题和不同的答案。一些人建议不要使用此功能,另一些人则表示无法“修复”。
我有:
table: course
fields: courseID, courseName
示例:表中的记录数:18。如果我删除记录 16、17 和 18 - 我希望输入的下一条记录的 courseID 为 16,但是它将是 19,因为最后输入的 courseID 是 18 .
我的 SQL 知识并不令人惊奇,但无论如何可以通过查询(或 phpMyAdmin 界面中的设置)刷新或更新此计数?
此表将与数据库中的其他表相关。
鉴于所有建议,我决定忽略这个“问题”。我将简单地删除和添加记录,同时让自动增量完成它的工作。我想数字是多少并不重要,因为它仅用作唯一标识符并且没有(如上所述)业务含义。
对于那些我可能对我的原始帖子感到困惑的人:我不想使用这个字段来知道我有多少记录。我只是希望数据库看起来整洁并具有更多的一致性。
【问题讨论】:
如果您只删除记录 16 会发生什么? 好点!我应该提到的。理想情况下,将所有内容上移一个? 我遇到的问题在某种程度上与您的问题相反。换句话说,我有一个ENTITY
表和一个ENTITY_LOG
表。每次我从ENTITY
表中插入、更新或删除某些内容时,我都会将该活动与实体的Id
一起记录到ENTITY_LOG
表中。最近有用户删除了ENTITY
表中的一堆条目;并在日志表中创建了相应的日志条目。今天,当另一个用户尝试在ENTITY
表中添加新条目时,生成的Id
恰好与之前删除的实体相同!我使用 JdbcTemplate 和 InnoDB。
@WebUser 我不明白为什么会这样。我很确定(并且期望)MySQL 会处理所有这些。不确定 JdbcTemplate 方面的事情 - 也许那里有什么东西在做它(?)。祝你好运!
@OmidTahouri,我完全同意这一点。这当然是一个奇怪的问题。因此,我将尝试复制问题并逐步执行代码。感谢您的确认!
【参考方案1】:
您尝试做的事情听起来很危险,因为这不是 AUTO_INCREMENT
的预期用途。
如果您真的想找到最低的未使用密钥值,请不要使用AUTO_INCREMENT
,并手动管理您的密钥。但是,这不是推荐的做法。
退一步问“为什么需要回收键值?” unsigned INT
(或BIGINT
)没有提供足够大的键空间吗?
在您的应用程序生命周期中,您真的会拥有超过 18,446,744,073,709,551,615
的唯一记录吗?
【讨论】:
你是对的。最好忽略它。考虑到这只是一个任务,我不会进入大量数字并且生命周期很短。 出于多种原因同意高度危险。一,在多用户系统中,您每秒可能有许多、数百、数千次更新,并且尝试重写 auto inc 可能会减慢系统速度或损害系统。另外两个开发人员可能不知道您正在这样做,并通过 id 链接记录从而破坏系统。等 好点。答案让我三思而后行。我一直在寻找 auto_incr 中的空白。因为我打算以某种方式循环遍历这些 ID。但我想最好只循环遍历行:) 所以根本不可能?希望在测试环境中执行此操作是有正当理由的。 当然是可能。 MySQL有schema,但对schema的理解并不丰富。语义是在应用程序中派生的。你有整数。不是教室标识符。它没有关于语义是什么的概念,你用它做什么,它如何与其他实体连接,外键不能在数据库之外应用于其他数据结构。因此,“使密钥集合理化”的想法在 MySQL 中并不是一个概念:这将属于应用程序的领域,但尝试这样做几乎总是一个坏主意。【参考方案2】:ALTER TABLE foo AUTO_INCREMENT=1
如果您已删除最近的条目,则应将其设置为使用下一个可用的最低条目。如中,只要没有 19,删除 16-18 将重置自动增量以使用 16。
编辑:我错过了关于 phpmyadmin 的内容。你也可以在那里设置它。转到表格屏幕,然后单击操作选项卡。那里有一个 AUTOINCREMENT
字段,您可以手动将其设置为您需要的任何内容。
【讨论】:
OP 可以这样做,但他不应该这样做。鉴于 OP 正在尝试做的事情,你应该重新考虑这个建议。 我不该告诉他如何布置他的数据库,或者如何做他的业务逻辑。他还在帖子中提到,他读过其他帖子/页面说这是一个坏主意,所以他知道这不是推荐的做法,但无论如何都会继续这样做。 这是目前对提问者唯一明确的问题提供的最直接的答案。不涉及“建议”。 始终是一个很好的“实践”,但如果您有理由反对它,OP 可能会错过,建议不要使用您的解决方案。 虽然这 是 一种不好的做法,但它是最好的答案,因为它实际上回答了 OP 的要求【参考方案3】:数据库中的主自动增量键用于唯一标识给定行,不应赋予任何业务含义。所以保留主键并添加另一列,例如courseOrder
。然后,当您从数据库中删除一条记录时,您可能需要发送一条额外的 UPDATE 语句,以减少所有 courseOrder
大于您当前删除的行的 courseOrder
列。
作为旁注,您永远不应该修改关系数据库中主键的值,因为可能有其他表将其作为外键引用,并且修改它可能会违反引用约束。
【讨论】:
他希望保持计数,而不是排序。 UPDATE 似乎没有必要。 好吧,如果他希望保持计数,则无需添加其他列。简单的count
聚合函数就可以完成这项工作。
好的,谢谢。在这么短的时间内有很多答案/cmets! :O 我正在尝试将它们全部纳入。我将研究计数功能:)【参考方案4】:
试试:
SET @num := 0;
更新 your_table SET id = @num := (@num+1);
ALTER TABLE
tableName
AUTO_INCREMENT = 1;
这将重置自动递增的值,然后在为其创建新值时计算每一行。
示例:之前
1:这里的第一个值 2:这里的第二个值 X : 删除值 4:表格的其余部分 5 : 其余的..所以表格将显示数组:1,2,4,5
例子:AFTER(如果你使用这个命令你会得到)
1:这里的第一个值 2:这里的第二个值 3:表格的其余部分 4 : 其余部分删除的值没有任何痕迹,其余的递增值继续使用这个新计数。
但是
-
如果您的代码中某处使用了自动增量值...也许这种归因会导致问题。
如果您不在代码中使用此值,一切都应该没问题。
【讨论】:
如果你想保留旧值并将 auto_increment 设置为最大值,那么你需要找到最大值: 1. 通过排序,取最后一行的值/ 2. 然后在您的代码中将自动增量设置为该最大值 +1。 真的很感激。它对解决我朋友的问题有很大帮助。在互联网上进行了大量搜索后,我找到了这个答案。【参考方案5】:您不应该依赖 AUTO_INCREMENT id 来告诉您表中有多少条记录。您应该使用SELECT COUNT(*) FROM course
。 ID 用于唯一标识课程并可用作其他表中的参考,因此您不应重复 ID,也不应寻求重置自动增量字段。
【讨论】:
我认为他认为这是某种错误。 MySQL 已经 20 岁了。这绝对不是疏忽。自动增量不回收密钥是有充分理由的。你是完全正确的迈克。【参考方案6】:我来这里是为了寻找标题问题"MySQL - Auto Increment after delete"
的答案,但我只能在问题中找到答案
通过使用类似的东西:
DELETE FROM table;
ALTER TABLE table AUTO_INCREMENT = 1;
注意Darin Dimitrov's answer 解释得非常好AUTO_INCREMENT
和它的用法。在做你可能会后悔的事情之前先看看那里。
PS:问题本身更多的是"Why you need to recycle key values?"
和Dolph's answer 覆盖。
【讨论】:
【参考方案7】:你可以像这样选择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
客户按消费金额排名。
【讨论】:
【参考方案8】:我可以想到很多您可能需要这样做的场景,尤其是在迁移或开发过程中。例如,我刚才必须通过交叉连接两个现有表来创建一个新表(作为复杂设置过程的一部分),然后我需要在事件之后添加一个主键。您可以删除现有的主键列,然后执行此操作。
ALTER TABLE my_table ADD `ID` INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`ID`);
对于实时系统,这不是一个好主意,尤其是如果有其他表具有指向它的外键。
【讨论】:
【参考方案9】:我有一个非常简单但很棘手的方法。
删除行时,您可以将 ID 保存到另一个临时表中。之后,当您将新数据插入主表时,您可以从临时表中搜索并选择 ID。所以在这里使用检查。如果临时表没有 ID,则计算最大 ID 到主表中,并将新 ID 设置为:new_ID = old_max_ID+1
。
注意:这里不能使用自动增量功能。
【讨论】:
【参考方案10】:您正在尝试做的事情非常危险。仔细考虑一下。自动增量的默认行为有一个很好的理由。
考虑一下:
删除一个表中与另一个表有关系的记录。由于审计原因,不能删除第二张表中对应的记录。该记录成为第一个表的孤立记录。如果将新记录插入到第一个表中,并且使用顺序主键,则该记录现在链接到孤立记录。显然,这很糟糕。通过使用自动递增的 PK,始终可以保证以前从未使用过的 id。这意味着孤儿仍然是孤儿,这是正确的。
【讨论】:
【参考方案11】:实际上有一种方法可以解决这个问题。首先删除 auto_incremented 主键列,然后再次添加,如下所示:
ALTER TABLE table_name DROP column_name;
ALTER TABLE table_name ADD column_name int not null auto_increment primary key first;
【讨论】:
【参考方案12】:您可能会考虑在删除后创建一个触发器,这样您就可以更新 autoincrement 的值以及所有看起来不像您想要看到的行的 ID 值。
因此,您可以使用同一个表,并且每当您删除一行时,触发器会自动修复自动增量。
【讨论】:
link【参考方案13】:您可以使用您的 mysql 客户端软件/脚本来指定删除所需记录后主键应从何处开始。
【讨论】:
【参考方案14】:绝对不推荐。如果您有一个包含多个表的大型数据库,您可能已经在表 2 中保存了一个用户 ID 作为 id。如果您重新排列表 1,那么预期的用户 ID 可能最终不会成为预期的表 2 id。
【讨论】:
【参考方案15】:MYSQL 查询 自动增量解决方案。当您在软件测试阶段插入许多记录时,它可以完美运行。现在您想向您的客户端实时启动您的应用程序,并且您想从 1 开始自动递增。
为了避免任何不必要的问题,为了更安全的一面
先导出.sql
文件。
然后按照以下步骤操作:
步骤 1) 首先创建现有表的副本 MySQL 命令创建副本:
CREATE TABLE new_Table_Name SELECT * FROM existing_Table_Name;
创建表的精确副本,其中包含除约束之外的所有行。
它不会将 Auto Increment 和 Primary Key 等约束复制到 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。
现在它将完美运行。新的第一条记录将采用 Auto Increment 列中的第一个值。
【讨论】:
【参考方案16】:这是解决问题的步骤。
在您的 .php
文件中,只需添加以下给出的查询:
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error)
die("Connection failed: " . $conn->connect_error);
//write the number or id you want to start with the next user in AUTO_INCREMENT
$sql = "ALTER TABLE `table_name` AUTO_INCREMENT = number";
$conn->query($sql);
?>
希望你的问题能得到解决。
【讨论】:
【参考方案17】: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");
【讨论】:
MySQL 是一个关系型数据库。当有多个表时,需要在表之间定义关系。这是使用 ID 列完成的。这种方法的问题在于,如果您开始重新编号表的索引,则会对与其有关系的所有表产生连锁反应。【参考方案18】:这里有一个功能可以解决您的问题
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();
【讨论】:
以上是关于MySQL中删除后自动增量的主要内容,如果未能解决你的问题,请参考以下文章