19

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

我想这是不可能的,但我还是会问。

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

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

问题是。有没有一种简单/简单的方法可以让新的插件填补这些“洞”?

4

7 回答 7

15

我同意@Aaron Digulla 和@Shane N 的观点。这些差距毫无意义。如果它们确实意味着什么,那就是有缺陷的数据库设计。时期。

话虽如此,如果您绝对需要填补这些漏洞,并且您至少运行 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 的任何其他引用以及它使用的任何外键。为此,您可能需要删除您拥有的任何外键约束,将表中的任何引用从 OldId 更新为 NewId,然后重新建立外键约束。

但是,我认为您不应该这样做,并且只需了解您的 Id 字段的存在仅用于引用记录,并且不应该具有任何特定相关性。

更新:添加更新 ID 的示例

例如:

假设您有以下 2 个表模式:

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 参考)。

接下来,我们需要使用新值更新父表和子表。我们将为此使用以下 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

就是这样。

于 2009-12-03T17:09:48.837 回答
7

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

于 2009-12-03T16:36:27.923 回答
6

您通常不需要关心差距。如果您要到达 ID 数据类型的末尾,那么 ALTER 表以升级到下一个最大的 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

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

于 2009-12-03T16:47:28.097 回答
2
INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

IFNULL对于零行计数跳过 null

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

于 2011-11-10T23:32:57.167 回答
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

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

请注意,在下一次插入期间,除非您重置标识列,否则数据库将创建一个巨大的间隙。

于 2009-12-03T16:32:42.440 回答
1

正如其他人所说,没关系,如果确实如此,那么您的数据库设计就有问题。但就我个人而言,我只是希望它们井井有条!

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

它首先在一个temp_id字段中完成(您将需要创建该字段),因此您可以在覆盖旧 ID 之前看到这一切都很好。酌情更换Tblid

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;

您现在将拥有一个temp_id包含所有闪亮新 ID 的字段。你可以通过简单的方式让他们生活:

UPDATE Tbl SET id = temp_id;

然后放下你的temp_id专栏。

我必须承认我不太确定它为什么会起作用,因为我本以为引擎会抱怨重复的 ID,但是当我运行它时却没有。

于 2017-12-05T05:25:46.133 回答
-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
)
于 2010-07-02T08:22:40.220 回答