重新排序/重置自动增量主键

Posted

技术标签:

【中文标题】重新排序/重置自动增量主键【英文标题】:Reorder / reset auto increment primary key 【发布时间】:2010-10-18 22:17:14 【问题描述】:

我有一个带有自动增量主键的 mysql 表。我删除了表格中间的一些行。例如,现在我在 ID 列中有这样的内容:12、13、14、19、20。我删除了 15、16、17 和 18 行。

我想重新分配/重置/重新排序主键,以便保持连续性,即将 19 设为 15,将 20 设为 16,依此类推。

我该怎么做?

【问题讨论】:

【参考方案1】:

尽管这个问题似乎很老了,但会为在这里搜索的人发布答案。

SET @count = 0;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

如果该列在其他表中用作外键,请确保在这些表中使用ON UPDATE CASCADE 而不是默认的ON UPDATE NO ACTION 作为外键关系。

此外,为了重置AUTO_INCREMENT计数,您可以立即发出以下语句。

ALTER TABLE `users` AUTO_INCREMENT = 1;

对于 MySQL,它会将值重置为 MAX(id) + 1

【讨论】:

这对于我的表来说是一个非常酷的解决方案,其中插入和删除了大量垃圾,我想节省索引空间。 mySQL Doc 建议不要这样做:“作为一般规则,除了在 SET 语句中,您不应该为用户变量赋值并在同一语句中读取该值。例如,递增一个变量,这没关系: SET @a = @a + 1; 对于其他语句,例如 SELECT,你可能会得到你期望的结果,但这并不能保证。在下面的语句中,你可能认为 MySQL 会评估先@a,然后再赋值:SELECT @a, @a:=@a+1, ...; 但是,涉及用户变量的表达式的求值顺序是不确定的。" @ReverseEMF:不。在 MySQL 表达式中,赋值的顺序是固定的。根据您引用的内容,MySQL 文档建议不要多次独立使用该变量。在上述情况下,由于单个赋值表达式“users.id” = @count:= @count + 1,表达式的计算必然会按照预定义的顺序进行。来自文档:“右侧的值可能是文字值、存储值的另一个变量或任何产生标量值的合法表达式” 这是一个非常昂贵的声明吗?它在多 GB 表中的表现如何?我害怕炸毁我的 ibdata1(长事务)并阻塞表太久。 @Stefan 在具有外键的表 (5MB) 上引用另一个具有 +2GB 数据的表,此脚本不会花费超过五分钟。该系统有一个ssd,所以我想它有很大帮助。 fk 有 ON UPDATE CASCADE【参考方案2】:

您可以删除主键列并重新创建它。然后应按顺序重新分配所有 id。

但是,在大多数情况下,这可能不是一个好主意。如果您有其他表具有该表的外键,那么它肯定不起作用。

【讨论】:

我还有其他表持有该表的外键,但我刚刚开始项目,所以对我来说没问题.. 谢谢! 如果您尝试开始接受您的 ID 并不总是连续的,这可能是最好的长期方案,否则当您开始从事更大的项目时,它真的会发疯! ALTER TABLE your_table AUTO_INCREMENT =1【参考方案3】:

要重置我的用户表的 ID,我使用以下 SQL 查询。上面已经说过,这会破坏您与任何其他表的任何关系。

ALTER TABLE `users` DROP `id`;
ALTER TABLE `users` AUTO_INCREMENT = 1;
ALTER TABLE `users` ADD `id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;

【讨论】:

在有 584k 行的 MyISAM 表上,大约需要 7.3 秒。 如果我有 100 票,我会一直投赞成票,因为它分解了像我这样的菜鸟的 sql 语句,有助于理解 第二行不是必须的还是我错了?它本身是从 1 开始的 第二行不需要。 @JorgeAugustoMorêradeMoura 记录的顺序不会改变【参考方案4】:

你可以简单地使用这个查询

alter table abc auto_increment = 1;

【讨论】:

这在这种情况下不起作用。对于 ISAM 表,它会将 autoinc 值设置为 max(id) + 1。对于 InnoDB,它什么也不做。请参阅更改表文档以更改 AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html @Ireeder 从 5.6 开始,innodb 的行为类似于 myisam 如果您有其他表具有该表的外键,那么这会破坏它们吗?【参考方案5】:
SET  @num := 0;

UPDATE your_table SET id = @num := (@num+1);

ALTER TABLE your_table AUTO_INCREMENT =1;

我觉得这样就可以了

【讨论】:

【参考方案6】:

或者,从 phpMyAdmin 中删除“AutoIncrement”标志,保存,再次设置并保存。这将重置它。

【讨论】:

抱歉,我无法使用当前的 phpmyadmin 版本进行测试。我的回答太老了……如果你对我投了反对票,你能修改它吗?【参考方案7】:
SELECT * from `user` ORDER BY `user_id`; 

SET @count = 0;

UPDATE `user`  SET `user_id` = @count:= @count + 1;

ALTER TABLE `user_id` AUTO_INCREMENT = 1;

如果你想order by

【讨论】:

【参考方案8】:

在 phpmyadmin 中

注意:如果您删除最后一行而不是中间行,这将起作用。

转到您的表->单击操作菜单->转到表选项->将 AUTO_INCREMENT 更改为您要开始的那个 no。

您的表格自动增量从该编号开始。

试试看。

【讨论】:

【参考方案9】:

这有效 - https://***.com/a/5437720/10219008.....but 如果您遇到问题“错误代码:1265。第 1 行的列 'id' 的数据被截断”...然后运行以下命令。在更新查询上添加忽略。

SET @count = 0;
set sql_mode = 'STRICT_ALL_TABLES';
UPDATE IGNORE web_keyword SET id = @count := (@count+1);

【讨论】:

【参考方案10】:

您可以删除该列的主键自动递增功能,然后在每次更新该列时预先运行一个查询来计算表中的所有行,然后运行一个循环遍历该行计数,插入每个值到相应的行,最后运行一个查询插入一个新行,该列的值是总行数加一。这将完美无缺,对于试图完成您的工作的人来说,这是最绝对的解决方案。以下是您可以用于该功能的代码示例:

$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("
    SELECT `rank`, `field1`, `field2`, `field3`, `field4`
        FROM (SELECT (@rank:=@rank+1) as `rank`, `field1`, `field2`, `field3`, `field4`
            FROM (SELECT * FROM `views`) a
            CROSS JOIN (SELECT @rank:=0) b
            ORDER BY rank ASC) c
");
while ($row = mysql_fetch_assoc($viewsrowsdata)) 
    $data[] = $row;

foreach ($data as $row) 
    $new_field_1 = (int)$row['rank'];
    $old_field_1 = (int)$row['field1'];
    mysql_query("UPDATE `table` SET `field_1` = $new_field_1 WHERE `field_1` = $old_field_1");

mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");

在这里,我创建了一个关联数组,我在选择查询中的查询附加到排名列上,它为每一行提供了一个从 1 开始的排名值。然后我遍历关联数组。

另一个选项是获取行数,运行基本的选择查询,获取关联数组并以相同的方式对其进行迭代,但添加了一个在每次迭代中更新的变量。这不太灵活,但会完成同样的事情。

$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("SELECT * FROM `table`");
$updated_key = 0;
while ($row = mysql_fetch_assoc($viewsrowsdata)) 
    $data[] = $row;

foreach ($data as $row) 
    $updated_key = $updated_key + 1;
    mysql_query("UPDATE `table` SET `field_1` = '$updated_key' WHERE `field_1` = '$row['field_1']'");

mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");

【讨论】:

【参考方案11】:

对于 InnoDB,执行此操作(这将删除表中的所有记录,首先创建一个 bakcup):

SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS ;
SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION ;
SET NAMES utf8 ;
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 ;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 ;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' ;
SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 ;
/* ================================================= */

drop table tablename;
CREATE TABLE `tablename` (
   table structure here!

) ENGINE=InnoDB AUTO_INCREMENT=  ai number to reset  DEFAULT CHARSET= char set here;



/* ================================================= */
SET SQL_MODE=@OLD_SQL_MODE ;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS ;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS ;
SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT ;
SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS ;
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION ;
SET SQL_NOTES=@OLD_SQL_NOTES ;

【讨论】:

【参考方案12】:

我也有同样的疑问,但无法对表格进行任何更改,我决定执行以下操作,因为我的 ID 没有超过变量 @count 中设置的最大数量:

SET @count = 40000000;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

SET @count = 0;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

ALTER TABLE `users` AUTO_INCREMENT = 1;

解决方案需要,但它是安全的,而且是必要的,因为我的表拥有外键,而数据在另一个表中。

【讨论】:

【参考方案13】:

最好的选择是更改列并删除 auto_increment 属性。然后发出另一个alter 语句并将auto_increment 放回该列。这会将计数重置为当前行的 max+1,从而保留对该表、数据库中其他表或该列的任何其他键使用的外键引用。

【讨论】:

【参考方案14】:

我的意见是创建一个名为 row_order 的新列。然后重新排序该列。我不接受对主键的更改。例如,如果订单列是banner_position,我做了这样的事情,这是为了删除、更新、创建横幅位置列。调用这个函数分别重新排序。

public function updatePositions()
    $offers = Offer::select('banner_position')->orderBy('banner_position')->get();
    $offersCount = Offer::max('banner_position');
    $range = range(1, $offersCount);

    $existingBannerPositions = [];
    foreach($offers as $offer)
        $existingBannerPositions[] = $offer->banner_position;
    
    sort($existingBannerPositions);
    foreach($existingBannerPositions as $key => $position)
        $numbersLessThanPosition = range(1,$position);
        $freshNumbersLessThanPosition = array_diff($numbersLessThanPosition, $existingBannerPositions);
        if(count($freshNumbersLessThanPosition)>0) 
            $existingBannerPositions[$key] = current($freshNumbersLessThanPosition);
            Offer::where('banner_position',$position)->update(array('banner_position'=> current($freshNumbersLessThanPosition)));
        
    

【讨论】:

【参考方案15】:

您也可以简单地避免使用数字 ID 作为主键。如果表格包含国家/地区信息,您可以使用国家/地区代码作为主要 id,或者您可以使用永久链接,例如,如果它包含文章。

您也可以简单地使用随机值或 MD5 值。所有这些选项都有其自身的好处,特别是在 IT 方面。数字 ID 很容易枚举。

【讨论】:

... 你的依据是什么?也许只是不制作像“complete_user_info_export.php?userid=34”这样的页面?在内部使用字符串或其他随机值作为索引/标识符是一个真的坏主意。它产生的问题多于它解决的问题(如果它甚至解决了任何问题) MD5 值是绝对最坏的可能性,因为两个不同的值或数据集可能产生相同的 MD5 值。所以我不会称之为解决方案。

以上是关于重新排序/重置自动增量主键的主要内容,如果未能解决你的问题,请参考以下文章

重新排序/重置自动增量主键

重新排序/重置自动增量主键

在 Microsoft SQL Server 2008 R2 中重置自动增量

在脚本mysql中重置自动增量列值

AUTOINCREMENT 自动增量

MySQL 从表中删除所有行并将 ID 重置为零