如何填写自动递增字段中的“漏洞”?

我读过一些关于这个post,但没有涵盖这个问题。

我猜这是不可能的,但我仍然会问。

我有一个超过50,000个寄存器的表。 这是一个旧表,发生了各种插入/删除操作。

也就是说,有大约300个寄存器有一些“漏洞”。 即:…,1340,1341,1660,1661,1662,…

问题是。 有没有一种简单/容易的方法来使新插入填补这些“洞”?

thx Paulo Bueno

你需要这个function的原因是什么? 你的数据库应该没有问题,如果你正在接近你的密钥的最大大小,只需使其无符号或更改字段types。

我同意@Aaron Digulla和@Shane N.这些差距是毫无意义的。 如果他们意味着什么,这是一个有缺陷的数据库devise。 期。

这就是说,如果你绝对需要填补这些漏洞,而且你正在运行至lessMySQL 3.23,你可以利用TEMPORARY TABLE来创build一个新的ID集。 这里的想法是,你要按照顺序将所有当前的IDselect到临时表中,如下所示:

 CREATE TEMPORARY TABLE NewIDs ( NewID INT UNSIGNED AUTO INCREMENT, OldID INT UNSIGNED ) INSERT INTO NewIDs (OldId) SELECT Id FROM OldTable ORDER BY Id ASC 

由于NewId列的AUTO INCREMENT属性,这会给你一个将你的旧Id映射到一个全新的Id的表格,这个Id本质上是连续的。

完成此操作后,您需要更新对“OldTable”中的Id以及其使用的任何外键的任何其他引用。 为此,您可能需要删除您拥有的任何外键约束,更新表中OldId到NewId的任何引用,然后重新设置外键约束。

但是,我认为你不应该做任何这样的事情,只要明白你的Id字段的存在只是为了引用logging,而不应该有任何特定的相关性。

更新:添加更新Id的示例

例如:

假设您有以下两个表格模式:

 CREATE TABLE Parent ( ParentId INT UNSIGNED AUTO INCREMENT, Value INT UNSIGNED, PRIMARY KEY (ParentId) ) CREATE TABLE Child ( ChildId INT UNSIGNED AUTO INCREMENT, ParentId INT UNSIGNED, PRIMARY KEY(ChildId), FOREIGN KEY(ParentId) REFERENCES Parent(ParentId) ) 

现在,差距出现在您的父表中。

为了更新您的父母和孩子的值,您首先创build一个临时表与映射:

 CREATE TEMPORARY TABLE NewIDs ( Id INT UNSIGNED AUTO INCREMENT, ParentID INT UNSIGNED ) INSERT INTO NewIDs (ParentId) SELECT ParentId FROM Parent ORDER BY ParentId ASC 

接下来,我们需要告诉MySQL忽略外键约束,所以我们可以正确地更新我们的值。 我们将使用这个语法:

 SET foreign_key_checks = 0; 

这会导致MySQL在更新值时忽略外键检查,但仍会强制使用正确的值types(请参阅MySQL参考以了解详细信息)。

接下来,我们需要用新的值更新我们的Parent和Child表。 我们将使用下面的UPDATE语句:

 UPDATE Parent, Child, NewIds SET Parent.ParentId = NewIds.Id, Child.ParentId = NewIds.Id WHERE Parent.ParentId = NewIds.ParentId AND Child.ParentId = NewIds.ParentId 

我们现在已经将我们所有的ParentId值正确地更新到了临时表中新的,有序的Id。 一旦完成,我们可以重新进行外键检查以保持参照完整性:

 SET foreign_key_checks = 1; 

最后,我们将放弃临时表来清理资源:

 DROP TABLE NewIds 

就是这样。

你通常不需要关心差距。 如果你到了ID的数据types的末尾,应该比较容易的将ALTER表升级到下一个最大的inttypes。

如果你绝对必须开始填补空白,这里是一个查询返回最低的可用ID(希望不是太慢):

 SELECT MIN(table0.id)+1 AS newid FROM table AS table0 LEFT JOIN table AS table1 ON table1.id=table0.id+1 WHERE table1.id IS NULL 

(如果需要并发插入,请记住使用事务和/或捕获重复键插入。)

 INSERT INTO prueba(id) VALUES ( (SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target)) 

IFNULL在零行数上跳过空值

添加目标为跳过错误mysql“错误子句FROM)

有一个简单的方法,但它不能很好地执行:只要尝试插入一个ID,当失败时,尝试下一个。

另外,select一个ID,当你没有得到结果,使用它。

如果你正在寻找一种方法来告诉数据库自动填补空白,那么这是不可能的。 而且,它不应该是必要的。 如果你觉得你需要它,那么你会滥用一个内部的技术密钥来实现某些目的,但这只是一个目的:允许你连接表。

[编辑]如果这不是主键,那么你可以使用这个更新声明:

 update ( select * from table order by reg_id -- this makes sure that the order stays the same ) set reg_id = x.nextval 

其中x是您必须创build的新序列。 这将重新编号保留订单的所有现有元素。 如果您有外键约束,这将失败。 如果你在任何地方引用这些ID而没有外键限制,它将会破坏你的数据库。

请注意,在下一次插入时,数据库将创build一个巨大的差距,除非您重置标识列。

您可能想要清理优先级列中的空白。 下面的方式将为优先级提供一个自动增量字段。 在同一个tabel上多余的左连接将确保它以和(在这种情况下)优先级相同的顺序被添加

 SET @a:=0; REPLACE INTO footable (id,priority) ( SELECT tbl2.id, @a FROM footable as tbl LEFT JOIN footable as tbl2 ON tbl2.id = tbl.id WHERE (select @a:=@a+1) ORDER BY tbl.priority )