我已经阅读了一些有关此内容的帖子,但没有一篇涉及此问题。
我想这不可能,但无论如何我都会问。
我有一个包含超过50,000个寄存器的表。这是一个旧表,其中发生了各种插入/删除操作。
也就是说,大约300个寄存器中有一些“漏洞”。即:......,1340,1341,1660,1661,1662,......
问题是。是否有一种简单/简单的方法可以使新的插入物填充这些“漏洞”?
THX Paulo Bueno
答案 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之前看到它们都很好。 1}}和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
)