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

时间:2009-12-03 16:28:16

标签: mysql auto-increment

我已经阅读了一些有关此内容的帖子,但没有一篇涉及此问题。

我想这不可能,但无论如何我都会问。

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

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

问题是。是否有一种简单/简单的方法可以使新的插入物填充这些“漏洞”?

THX Paulo Bueno

7 个答案:

答案 0 :(得分:13)

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

话虽这么说,如果你绝对需要填补这些漏洞, AND 你至少运行MySQL 3.23,你可以利用TEMPORARY TABLE来创建一组新的ID。这里的想法是,您将按顺序将所有当前ID选择到临时表中:

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。

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

但是,我认为您不应该 ANY ,并且只是了解您的Id字段仅用于引用记录,并且 NOT 有任何特定的相关性。

更新:添加更新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)
)

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

为了更新Parent和Child中的值,首先要创建一个包含映射的临时表:

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在更新值时忽略外键检查,但仍会强制使用正确的值类型(有关详细信息,请参阅MySQL reference)。

接下来,我们需要使用新值更新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

就是这样。

答案 1 :(得分:8)

您需要此功能的原因是什么?您的数据库应该没有间隙,如果您正在接近密钥的最大大小,只需将其设置为无符号或更改字段类型。

答案 2 :(得分:4)

您通常不需要关心差距。如果您到达ID的数据类型的末尾,则应该相对容易地将表升级为下一个最大的int类型。

如果你绝对必须开始填补空白,这里是一个返回最低可用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

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

答案 3 :(得分:2)

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

IFNULL for zero rows count

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

答案 4 :(得分:1)

有一种简单的方法,但效果不佳:只需尝试插入一个id,当失败时,尝试下一个。

或者,选择一个ID,当您没有得到结果时,请使用它。

如果您正在寻找一种方法来告诉数据库自动填补空白,那么这是不可能的。而且,它永远不应该是必要的。如果你觉得自己需要它,那么你就会滥用内部技术密钥来实现它的唯一目的:允许你加入表格。

[编辑]如果这不是主键,则可以使用此更新语句:

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

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

请注意,在下一次插入期间,除非您重置标识列,否则数据库将产生巨大的差距。

答案 5 :(得分:0)

正如其他人所说的那样,没关系,如果确实如此,那么数据库设计就会出现问题。但就我个人而言,我只是喜欢他们的顺序!

以下是一些SQL,它将以相同的顺序重新创建您的ID,但没有间隙。

首先在temp_id字段(您需要创建)中完成,因此您可以在覆盖旧ID之前看到它们都很好。 Tbl视情况而定。

id

现在,您将拥有一个SELECT @i:=0; UPDATE Tbl JOIN ( SELECT id FROM Tbl ORDER BY id ) t2 ON Tbl.id = t2.id SET temp_id = @i:=@i+1; 字段,其中包含所有闪亮的新ID。你可以简单地让它们生活:

temp_id

然后删除UPDATE Tbl SET id = temp_id; 列。

我必须承认我不太清楚它为什么会起作用,因为我原本期望引擎抱怨重复的ID,但是当我运行它时它没有。

答案 6 :(得分:-1)

您可能希望清理优先级列中的空白。 下面的方法将为优先级提供自动增量字段。 在同一个表格上的额外左连接将确保以与(在本例中)优先级相同的顺序添加

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
)