Réorganiser / réinitialiser la clé primaire à incrémentation automatique

127

J'ai une table MySQL avec une clé primaire à incrémentation automatique. J'ai supprimé quelques lignes au milieu du tableau. Maintenant, j'ai, par exemple, quelque chose comme ça dans la colonne ID: 12, 13, 14, 19, 20. J'ai supprimé les 15, 16, 17 et 18 lignes.

Je veux réattribuer / réinitialiser / réorganiser la clé primaire pour avoir la continuité, c'est-à-dire faire le 19 a 15, le 20 a 16, et ainsi de suite.

Comment puis-je le faire?

Jonathan
la source

Réponses:

95

Vous pouvez supprimer la colonne de clé primaire et la recréer. Tous les identifiants doivent ensuite être réaffectés dans l'ordre.

Cependant, c'est probablement une mauvaise idée dans la plupart des situations. Si vous avez d'autres tables qui ont des clés étrangères pour cette table, cela ne fonctionnera certainement pas.

Tom Haigh
la source
J'ai d'autres tables qui contiennent une clé étrangère pour cette table, mais je ne fais que commencer le projet donc c'est OK pour moi .. Merci!
Jonathan
65
Ce serait peut-être mieux à long terme si vous essayez et commencez à accepter que vos identifiants ne soient pas toujours séquentiels, sinon lorsque vous commencez à travailler sur de plus gros projets, cela deviendra vraiment fou!
Ciaran McNulty
8
ALTER TABLE votre_table AUTO_INCREMENT = 1
Sinac
356

Même si cette question semble assez ancienne, affichera une réponse pour quelqu'un qui cherche ici.

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

Si la colonne est utilisée comme clé étrangère dans d'autres tables, assurez-vous d'utiliser à la ON UPDATE CASCADEplace de la valeur ON UPDATE NO ACTIONpar défaut pour la relation de clé étrangère dans ces tables.

En outre, pour réinitialiser le AUTO_INCREMENTdécompte, vous pouvez immédiatement émettre l'instruction suivante.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Pour MySQL, la valeur sera réinitialisée MAX(id) + 1.

Anshul
la source
Ceci avec des clés étrangères est une solution très cool pour ma table où beaucoup de fichiers indésirables sont insérés et supprimés et je veux économiser de l'espace d'index.
mukunda
1
mySQL Doc déconseille ceci: "En règle générale, sauf dans les instructions SET, vous ne devez jamais attribuer une valeur à une variable utilisateur et lire la valeur dans la même instruction. Par exemple, pour incrémenter une variable, c'est correct: SET @a = @a + 1; Pour d'autres instructions, telles que SELECT, vous pouvez obtenir les résultats escomptés, mais ce n'est pas garanti. Dans l'instruction suivante, vous pourriez penser que MySQL évaluera d'abord @a, puis effectuera une affectation second: SELECT @a, @a: = @ a + 1, ...; Cependant, l'ordre d'évaluation des expressions impliquant des variables utilisateur n'est pas défini. "
ReverseEMF
3
@ReverseEMF: Non. L'ordre d'affectation est fixé dans les expressions MySQL. D'après ce que vous avez cité, la documentation MySQL déconseille l'utilisation multiple indépendante de la variable. Dans le cas ci-dessus, l'évaluation de l'expression est appelée à se produire dans un ordre prédéfini en raison d'une seule expression d'affectation `` .user id` = @count: = @count + 1`. De la documentation: "La valeur sur le côté droit peut être une valeur littérale, une autre variable stockant une valeur, ou toute expression légale qui donne une valeur scalaire"
Anshul
1
Est-ce une déclaration très coûteuse? Comment fonctionnerait-il dans une table de plusieurs gigaoctets? J'ai peur de faire sauter mon ibdata1 (transaction longue) et de bloquer la table trop longtemps.
Stefan
1
@Stefan Sur une table (5 Mo) avec des clés étrangères qui en référence une autre avec + 2 Go de données, ce script n'a pas pris plus de cinq minutes. Le système a un SSD, donc je suppose que cela a beaucoup aidé. La fk a eu la CASCADE ON UPDATE
fernandezr
60

Pour réinitialiser les ID de ma table User, j'utilise la requête SQL suivante. Il a été dit ci-dessus que cela ruinerait toutes les relations que vous pourriez avoir avec d'autres tables.

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;
Ryan
la source
Sur une table MyISAM avec 584 000 lignes, cela a pris environ 7,3 secondes.
user1278519
3
Si j'avais eu 100 votes, j'aurais voté tout le temps car il décompose les déclarations SQL pour un noob comme moi, ce qui aide à la compréhension
repzero
1
La deuxième ligne n'est pas nécessaire ou ai-je tort? Il commence par 1 seul
``
deuxième ligne pas nécessaire.
KawaiKx
1
@ JorgeAugustoMorêradeMoura l'ordre des records ne sera pas changé
Ryan
31

Vous pouvez simplement utiliser cette requête

alter table abc auto_increment = 1;
Aaron W.
la source
2
Cela ne fonctionnera pas dans ce cas. Pour les tables ISAM, il définira la valeur autoinc sur max (id) + 1. Pour InnoDB, il ne fera rien. Voir la documentation de la table alter pour changer AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder
3
@Ireeder à partir de la version 5.6, le comportement d'innodb est similaire à celui de myisam
Anshul
Si vous avez d'autres tables qui ont des clés étrangères pour cette table, cela les cassera-t-il?
Kyle Vassella
15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Je pense que ça va le faire

bagyei37
la source
12

Ou, depuis PhpMyAdmin, supprimez l'indicateur "AutoIncrement", enregistrez-le, réglez-le à nouveau et enregistrez-le. Cela le réinitialise.

lbrutti
la source
Désolé, je ne peux pas tester avec les versions actuelles de phpmyadmin. Ma réponse est assez ancienne ... Si vous m'avez déconseillé, pouvez-vous l'amender?
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;

si tu veux order by

Shubham Malik
la source
1

en phpmyadmin

Remarque: cela fonctionnera si vous supprimez les dernières lignes et non les lignes du milieu.

aller à votre table-> cliquer sur le menu des opérations-> aller aux options de la table-> changer AUTO_INCREMENT en ce no d'où vous voulez commencer.

l'auto-incrémentation de votre table part de ce non.

essayez-le. entrez la description de l'image ici

Anjani Barnwal
la source
0

Vous pouvez supprimer la fonctionnalité d'incrémentation automatique de clé primaire de cette colonne, puis chaque fois que vous mettez à jour cette colonne, exécutez une requête qui comptera toutes les lignes de la table, puis exécutez une boucle qui itère à travers ce nombre de lignes en insérant chaque valeur dans le ligne respective, et enfin exécutez une requête insérant une nouvelle ligne avec la valeur de cette colonne étant le nombre total de lignes plus un. Cela fonctionnera parfaitement et c'est la solution la plus absolue pour quelqu'un qui essaie d'accomplir ce que vous êtes. Voici un exemple de code que vous pouvez utiliser pour la fonction:

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

Ici, j'ai créé un tableau associatif que j'avais ajouté sur une colonne de rang avec la requête dans une requête de sélection, qui donnait à chaque ligne une valeur de rang commençant par 1. J'ai ensuite itéré à travers le tableau associatif.

Une autre option aurait été d'obtenir le nombre de lignes, d'exécuter une requête de sélection de base, d'obtenir le tableau associatif et de l'itérer de la même manière, mais avec une variable supplémentaire qui se met à jour à chaque itération. Ceci est moins flexible mais accomplira la même chose.

$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')");
Willy
la source
0

pour InnoDB, faites ceci (cela supprimera tous les enregistrements d'une table, créera d'abord une 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 ;
Luis
la source
0

J'avais les mêmes doutes, mais je n'ai pas pu apporter de modifications sur la table, j'ai décidé de faire ce qui suit en voyant mon identifiant ne pas dépasser le nombre maximum défini dans la variable @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;

La solution prend, mais c'est sûr et c'était nécessaire car ma table possédait des clés étrangères avec des données dans une autre table.

Rodrigo Prazim
la source
0

Le meilleur choix est de modifier la colonne et de supprimer l'attribut auto_increment. Puis émettez une autre instruction alter et remettez auto_increment sur la colonne. Cela réinitialisera le nombre au maximum + 1 des lignes actuelles et conservera ainsi les références de clé étrangère à cette table, à partir d'autres tables de votre base de données ou à toute autre utilisation de clé pour cette colonne.

Grwww
la source
0

Mon avis est de créer une nouvelle colonne appelée row_order. puis réorganisez cette colonne. Je n'accepte pas les modifications apportées à la clé primaire. À titre d'exemple, si la colonne de commande est banner_position, j'ai fait quelque chose comme ceci, c'est pour supprimer, mettre à jour, créer la colonne de position de bannière. Appelez cette fonction pour les réorganiser respectivement.

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)));
        }
    }
}
Viraj Amarasinghe
la source
0

Cela fonctionne - https : //stackoverflow.com/a/5437720/10219008..... mais si vous rencontrez un problème 'Code d'erreur: 1265. Données tronquées pour la colonne' id 'à la ligne 1' ... Puis exécutez le suivant. Ajout d'ignorer sur la requête de mise à jour.

SET @count = 0;
set sql_mode = 'STRICT_ALL_TABLES';
UPDATE IGNORE web_keyword SET id = @count := (@count+1);
Anush BM
la source
-2

Vous pouvez également simplement éviter d'utiliser des identifiants numériques comme clé primaire. Vous pouvez utiliser les codes de pays comme identifiant principal si le tableau contient des informations sur les pays, ou vous pouvez utiliser des permaliens, s'il contient des articles par exemple.

Vous pouvez également simplement utiliser une valeur aléatoire ou MD5. Toutes ces options ont leurs propres avantages, en particulier sur le secteur informatique. Les identifiants numériques sont faciles à énumérer.

Chris Russo
la source
1
... Sur quoi vous basez-vous? Peut-être ne faites-vous pas des pages comme "complete_user_info_export.php? Userid = 34"? Utiliser en interne une chaîne ou une autre valeur aléatoire comme index / identifiant est une très mauvaise idée. Cela crée plus de problèmes qu'il n'en résout (s'il résout même des problèmes)
Rob
La valeur MD5 est la pire possibilité absolue car il est possible que deux valeurs ou ensembles de données différents produisent la même valeur MD5. Je n'appellerais donc pas cela une solution.
David