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


127

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

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

我该怎么做?

Answers:


95

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

但是,在大多数情况下,这可能不是一个好主意。如果您还有其他具有此表外键的表,则绝对不能使用。


我还有其他持有该表外键的表,但是我只是在启动项目,所以对我来说还可以。.谢谢!
乔纳森

65
如果您尝试并开始接受ID并非总是顺序的,那可能是最好的长期选择,否则当您开始从事较大的项目时,它真的会被淘汰!
Ciaran McNulty

8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac

356

即使这个问题似乎已经很老了,也将为到达此处搜索的人发布答案。

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


对于我的表,此表带有外键是一个非常酷的解决方案,在该表中插入和删除了很多垃圾,我想节省索引空间。
mukunda 2014年

1
mySQL Doc建议不要这样做:“通常,除了在SET语句中,永远不要为用户变量分配值,并且不要在同一条语句中读取该值。例如,要增加变量,可以这样:SET @a = @a + 1;对于其他语句,例如SELECT,您可能会得到期望的结果,但这不能保证。在下面的语句中,您可能认为MySQL将首先评估@a然后执行赋值第二:SELECT @a,@a:= @ a + 1,...;但是,涉及用户变量的表达式的求值顺序是不确定的。“
ReverseEMF 2015年

3
@ReverseEMF:否。分配的顺序在MySQL表达式中是固定的。根据您的引用,MySQL文档建议不要多次独立使用变量。在上述情况下,由于单个赋值表达式``users .id` = @count:= @count + 1` ,表达式的计算注定会以预定义的顺序发生。在文档中:“右侧的值可能是文字值,另一个存储值的变量或任何产生标量值的合法表达式”
Anshul 2015年

1
这是一个非常昂贵的声明吗?在数千兆字节的表中它将如何执行?我担心会炸毁ibdata1(长事务)并将表阻塞太长时间。
Stefan's

1
@Stefan在具有外键的表(5MB)上,该表引用了另一个具有+ 2GB数据的表,该脚本花费了不到五分钟的时间。该系统有一个固态硬盘,所以我想它很有帮助。fk拥有ON UPDATE CASCADE
Fernandezr

60

要重置用户表的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秒。
user1278519

3
如果我获得100票,我会一直投票赞成,因为它破坏了像我这样帮助理解的
菜鸟

1
第二行是不必要的,还是我错了?它以1
单独

第二行没有必要。
KawaiKx

1
@JorgeAugustoMorêradeMoura记录的顺序不会改变
Ryan

31

您可以简单地使用此查询

alter table abc auto_increment = 1;

2
在这种情况下,这将无法正常工作。对于ISAM表,它将autoinc值设置为max(id)+1。对于InnoDB,它将不执行任何操作。请参见ALTER TABLE文档改变AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder

3
@Ireeder从5.6开始,对于innodb的行为类似于myisam的行为
Anshul 2014年

如果您还有其他具有该表外键的表,那么这会破坏它们吗?
凯尔·瓦塞拉

15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

我认为这可以做到


12

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


抱歉,我无法使用当前的phpmyadmin版本进行测试。我的回答已经很老了...如果您不赞成我,可以请您修改它吗?
lbrutti

3
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


1

在phpmyadmin中

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

转到表格->单击操作菜单->转到表格选项->将AUTO_INCREMENT更改为您要从其开始的位置。

您的表自动增量从此开始。

试试吧。 在此处输入图片说明


0

您可以删除该列的主键自动递增功能,然后在每次更新该列时先进行查询,该查询将对表中的所有行进行计数,然后运行循环遍历该行计数的循环,将每个值插入到相应的行,最后运行查询以插入新行,该列的值为总行数加一。这将完美地工作,并且是尝试实现您的目标的人的绝对解决方案。这是您可以用于该函数的代码示例:

$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')");

0

对于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 ;

0

我也有同样的疑问,但是无法在表上进行任何更改,因此,在看到我的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;

该解决方案可以解决问题,但这是安全的,而且是必需的,因为我的表拥有外键,而另一个表中的数据也是如此。


0

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


0

我的意见是创建一个名为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)));
        }
    }
}

0

此作品- https://stackoverflow.com/a/5437720/10219008.....but如果你遇到一个问题“错误代码:1265年数据被截断为列‘’第1行”身份证......然后运行下列。在更新查询上添加忽略。

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

-2

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

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


1
...您以此为基础吗?也许只是不制作“ complete_user_info_export.php?userid = 34”之类的页面?在内部使用字符串或其他随机值作为索引/标识符是一个非常糟糕的主意。它产生的问题比解决的问题还要多(如果它甚至可以解决任何问题)
Rob

MD5值绝对是最坏的可能性,因为两个不同的值或数据集可能会产生相同的MD5值。所以我不会称之为解决方案。
大卫,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.