Primärschlüssel für automatische Inkrementierung neu anordnen / zurücksetzen

127

Ich habe eine MySQL-Tabelle mit einem Auto-Inkrement-Primärschlüssel. Ich habe einige Zeilen in der Mitte der Tabelle gelöscht. Jetzt habe ich zum Beispiel so etwas in der ID-Spalte: 12, 13, 14, 19, 20. Ich habe die Zeilen 15, 16, 17 und 18 gelöscht.

Ich möchte den Primärschlüssel neu zuweisen / zurücksetzen / neu anordnen, damit ich Kontinuität habe, dh die 19 a 15, die 20 a 16 und so weiter.

Wie kann ich es tun?

Jonathan
quelle

Antworten:

95

Sie können die Primärschlüsselspalte löschen und neu erstellen. Alle IDs sollten dann der Reihe nach neu zugewiesen werden.

Dies ist jedoch in den meisten Situationen wahrscheinlich eine schlechte Idee. Wenn Sie andere Tabellen mit Fremdschlüsseln für diese Tabelle haben, funktioniert dies definitiv nicht.

Tom Haigh
quelle
Ich habe andere Tabellen, die einen Fremdschlüssel für diese Tabelle enthalten, aber ich starte gerade das Projekt, damit es für mich in Ordnung ist. Danke!
Jonathan
65
Es könnte langfristig am besten sein, wenn Sie versuchen zu akzeptieren, dass Ihre IDs nicht immer sequentiell sind. Andernfalls wird es wirklich verrückt, wenn Sie anfangen, an größeren Projekten zu arbeiten!
Ciaran McNulty
8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac
356

Auch wenn diese Frage ziemlich alt zu sein scheint, wird eine Antwort für jemanden posten, der hier auf der Suche greift.

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

Wenn die Spalte in anderen Tabellen als Fremdschlüssel verwendet wird, stellen Sie sicher, dass Sie ON UPDATE CASCADEanstelle der Standardeinstellung ON UPDATE NO ACTIONfür die Fremdschlüsselbeziehung in diesen Tabellen verwenden.

Um die AUTO_INCREMENTAnzahl zurückzusetzen, können Sie außerdem sofort die folgende Anweisung ausgeben.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Bei MySQLs wird der Wert auf zurückgesetzt MAX(id) + 1.

Anshul
quelle
Dies mit Fremdschlüsseln ist eine sehr coole Lösung für meine Tabelle, bei der viel Müll eingefügt und gelöscht wird und ich Indexplatz sparen möchte.
Mukunda
1
mySQL Doc rät davon ab: "Im Allgemeinen sollten Sie, anders als in SET-Anweisungen, einer Benutzervariablen niemals einen Wert zuweisen und den Wert innerhalb derselben Anweisung lesen. Um beispielsweise eine Variable zu erhöhen, ist dies in Ordnung: SET @a = @a + 1; Bei anderen Anweisungen, wie z. B. SELECT, erhalten Sie möglicherweise die erwarteten Ergebnisse, dies ist jedoch nicht garantiert. In der folgenden Anweisung wird möglicherweise angenommen, dass MySQL zuerst @a auswertet und dann eine Zuweisung vornimmt zweitens: SELECT @a, @a: = @ a + 1, ...; Die Reihenfolge der Auswertung für Ausdrücke mit Benutzervariablen ist jedoch undefiniert. "
ReverseEMF
3
@ReverseEMF: Nein. Die Reihenfolge der Zuweisung ist in MySQL-Ausdrücken festgelegt. Nach Ihren Angaben rät die MySQL-Dokumentation von einer mehrfachen unabhängigen Verwendung der Variablen ab. Im obigen Fall muss die Auswertung des Ausdrucks aufgrund eines einzelnen Zuweisungsausdrucks `` users .id` = @count: = @count + 1` in einer vordefinierten Reihenfolge erfolgen. Aus der Dokumentation: "Der Wert auf der rechten Seite kann ein Literalwert, eine andere Variable, die einen Wert speichert, oder ein beliebiger rechtlicher Ausdruck sein, der einen skalaren Wert ergibt"
Anshul,
1
Ist das eine sehr teure Aussage? Wie würde es in einer Multi-Gigabyte-Tabelle funktionieren? Ich habe Angst, meine ibdata1 (lange Transaktion) in die Luft zu jagen und die Tabelle zu lange zu blockieren.
Stefan
1
@Stefan Bei einer Tabelle (5 MB) mit Fremdschlüsseln, die auf eine andere Tabelle mit + 2 GB Daten verweist, dauerte dieses Skript nicht länger als fünf Minuten. Das System hat eine SSD, also nehme ich an, dass es sehr geholfen hat. Die fk hatte die ON UPDATE CASCADE
fernandezr
60

Um die IDs meiner Benutzertabelle zurückzusetzen, verwende ich die folgende SQL-Abfrage. Es wurde oben gesagt, dass dies alle Beziehungen ruinieren wird, die Sie möglicherweise mit anderen Tabellen haben.

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
quelle
Bei einer MyISAM-Tabelle mit 584.000 Zeilen dauerte es ungefähr 7,3 Sekunden.
user1278519
3
Wenn ich 100 Stimmen hätte, hätte ich dies die ganze Zeit abgestimmt, da es die SQL-Aussagen für einen Noob wie mich aufschlüsselt, die zum Verständnis
beitragen
1
Die zweite Zeile ist nicht notwendig oder irre ich mich? Es beginnt mit 1 für sich allein ,,,, für InnoDB hat es für mich
getan
zweite Zeile nicht erforderlich.
KawaiKx
1
@ JorgeAugustoMorêradeMoura die Reihenfolge der Aufzeichnungen wird nicht geändert
Ryan
31

Sie können diese Abfrage einfach verwenden

alter table abc auto_increment = 1;
Aaron W.
quelle
2
Dies wird in diesem Fall nicht funktionieren. Für ISAM-Tabellen wird der Autoinc-Wert auf max (id) + 1 gesetzt. Für InnoDB wird nichts unternommen. Siehe alter table docs zum Ändern von AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder
3
@Ireeder ab 5.6 Verhalten für innodb ist ähnlich wie das von myisam
Anshul
Wenn Sie andere Tabellen mit Fremdschlüsseln für diese Tabelle haben, werden diese dann beschädigt?
Kyle Vassella
15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Ich denke, das wird es tun

bagyei37
quelle
12

Oder entfernen Sie in PhpMyAdmin das Flag "AutoIncrement", speichern Sie es, setzen Sie es erneut und speichern Sie es. Dadurch wird es zurückgesetzt.

lbrutti
quelle
Entschuldigung, ich kann nicht mit aktuellen phpmyadmin-Versionen testen. Meine Antwort ist ziemlich alt ... Wenn Sie mich herabgestimmt haben, können Sie sie bitte ändern?
Brutti
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;

Wenn du möchtest order by

Shubham Malik
quelle
1

in phpmyadmin

Hinweis: Dies funktioniert, wenn Sie die letzten Zeilen und nicht die mittleren Zeilen löschen.

Gehen Sie zu Ihrer Tabelle -> klicken Sie auf das Operationsmenü -> Gehen Sie zu Tabellenoptionen -> ändern Sie AUTO_INCREMENT in das Nein, von wo aus Sie beginnen möchten.

Ihre Tischautoinkrementierung beginnt mit dieser Nr.

Versuch es. Geben Sie hier die Bildbeschreibung ein

Anjani Barnwal
quelle
0

Sie können die automatische Inkrementierungsfunktion des Primärschlüssels dieser Spalte entfernen. Führen Sie dann jedes Mal, wenn Sie diese Spalte aktualisieren, eine Abfrage aus, die alle Zeilen in der Tabelle zählt, und führen Sie dann eine Schleife aus, die diese Zeilenanzahl durchläuft und jeden Wert in die Spalte einfügt Führen Sie schließlich eine Abfrage aus, in der eine neue Zeile eingefügt wird, wobei der Wert dieser Spalte die Gesamtzahl der Zeilen plus eins ist. Dies funktioniert einwandfrei und ist die absoluteste Lösung für jemanden, der versucht, das zu erreichen, was Sie sind. Hier ist ein Beispiel für Code, den Sie für die Funktion verwenden können:

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

Hier habe ich ein assoziatives Array erstellt, das ich an eine Rangspalte mit der Abfrage innerhalb einer ausgewählten Abfrage angehängt habe, wodurch jeder Zeile ein Rangwert zugewiesen wurde, der mit 1 beginnt. Anschließend habe ich das assoziative Array durchlaufen.

Eine andere Option wäre gewesen, die Zeilenanzahl abzurufen, eine grundlegende Auswahlabfrage auszuführen, das assoziative Array abzurufen und es auf dieselbe Weise zu iterieren, jedoch mit einer hinzugefügten Variablen, die bei jeder Iteration aktualisiert wird. Dies ist weniger flexibel, erreicht aber das Gleiche.

$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
quelle
0

Führen Sie für InnoDB dies aus (dies entfernt alle Datensätze aus einer Tabelle, machen Sie zuerst einen 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
quelle
0

Ich hatte die gleichen Zweifel, konnte aber keine Änderungen an der Tabelle vornehmen. Nachdem ich festgestellt hatte, dass meine ID die in der Variablen @count festgelegte maximale Anzahl nicht überschritt, entschied ich mich für Folgendes:

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;

Die Lösung dauert, aber es ist sicher und es war notwendig, weil meine Tabelle Fremdschlüssel mit Daten in einer anderen Tabelle besaß.

Rodrigo Prazim
quelle
0

Am besten ändern Sie die Spalte und entfernen das Attribut auto_increment. Geben Sie dann eine weitere alter-Anweisung aus und setzen Sie auto_increment wieder in die Spalte. Dadurch wird die Anzahl auf das Maximum + 1 der aktuellen Zeilen zurückgesetzt, und Fremdschlüsselverweise auf diese Tabelle, aus anderen Tabellen in Ihrer Datenbank oder andere Schlüsselverwendungen für diese Spalte bleiben erhalten.

Grwww
quelle
0

Meine Meinung ist, eine neue Spalte namens row_order zu erstellen. Ordnen Sie dann diese Spalte neu an. Ich akzeptiere die Änderungen am Primärschlüssel nicht. Wenn die Auftragsspalte beispielsweise banner_position lautet, habe ich Folgendes ausgeführt: Dies dient zum Löschen, Aktualisieren und Erstellen einer Bannerpositionsspalte. Rufen Sie diese Funktion auf, um sie jeweils neu zu ordnen.

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
quelle
0

Dies funktioniert - https://stackoverflow.com/a/5437720/10219008.....but, wenn Sie auf das Problem 'Fehlercode: 1265 "stoßen. Daten für Spalte' id 'in Zeile 1 abgeschnitten' ... Führen Sie dann aus folgende. Hinzufügen von Ignorieren für die Update-Abfrage.

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

Sie können auch einfach vermeiden, numerische IDs als Primärschlüssel zu verwenden. Sie können Ländercodes als primäre ID verwenden, wenn die Tabelle Länderinformationen enthält, oder Sie können Permalinks verwenden, wenn sie beispielsweise Artikel enthält.

Sie können auch einfach einen zufälligen oder einen MD5-Wert verwenden. Alle diese Optionen haben ihre eigenen Vorteile, insbesondere im IT-Bereich. numerische IDs sind einfach aufzuzählen.

Chris Russo
quelle
1
... Worauf stützen Sie sich? Vielleicht machen Sie einfach keine Seiten wie "complete_user_info_export.php? Userid = 34"? Die interne Verwendung einer Zeichenfolge oder eines anderen zufälligen Werts als Index / Bezeichner ist eine wirklich schlechte Idee. Es schafft mehr Probleme als es löst (wenn es überhaupt irgendwelche Probleme löst)
Rob
Der MD5-Wert ist die absolut schlechteste Möglichkeit, da möglicherweise zwei verschiedene Werte oder Datensätze denselben MD5-Wert erzeugen. Ich würde es also nicht als Lösung bezeichnen.
David