Riordina / ripristina chiave primaria incremento automatico


127

Ho una tabella MySQL con una chiave primaria di incremento automatico. Ho eliminato alcune righe al centro della tabella. Ora ho, ad esempio, qualcosa del genere nella colonna ID: 12, 13, 14, 19, 20. Ho eliminato le 15, 16, 17 e 18 righe.

Voglio riassegnare / resettare / riordinare la chiave primaria in modo da avere continuità, ovvero rendere 19 a 15, 20 a 16 e così via.

Come posso farlo?

Risposte:


95

È possibile eliminare la colonna chiave primaria e ricrearla. Tutti gli ID dovrebbero quindi essere riassegnati in ordine.

Tuttavia, questa è probabilmente una cattiva idea nella maggior parte delle situazioni. Se hai altre tabelle con chiavi esterne a questa tabella, sicuramente non funzionerà.


Ho altre tabelle che contengono una chiave esterna per questa tabella, ma sto appena iniziando il progetto, quindi per me va bene .. Grazie!
Jonathan,

65
Potrebbe essere il migliore a lungo termine se provi ad accettare che i tuoi ID non saranno sempre sequenziali, altrimenti quando inizi a lavorare su progetti più grandi diventerà davvero pazzo!
Ciaran McNulty,

8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac

356

Anche se questa domanda sembra essere piuttosto vecchia, pubblicherà una risposta per qualcuno che arriva qui a cercare.

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

Se la colonna viene utilizzata come chiave esterna in altre tabelle, assicurarsi di utilizzare al ON UPDATE CASCADEposto del valore predefinito ON UPDATE NO ACTIONper la relazione di chiave esterna in tali tabelle.

Inoltre, per ripristinare il AUTO_INCREMENTconteggio, è possibile emettere immediatamente la seguente dichiarazione.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Per MySQL reimposterà il valore su MAX(id) + 1.


Questo con le chiavi esterne è una soluzione molto interessante per la mia tabella in cui un sacco di posta indesiderata viene inserita ed eliminata e voglio risparmiare spazio sull'indice.
mukunda,

1
mySQL Doc sconsiglia questo: "Come regola generale, oltre alle istruzioni SET, non si dovrebbe mai assegnare un valore a una variabile utente e leggere il valore all'interno della stessa istruzione. Ad esempio, per incrementare una variabile, va bene: SET @a = @a + 1; Per altre istruzioni, come SELEZIONA, potresti ottenere i risultati che ti aspetti, ma ciò non è garantito. Nella seguente dichiarazione, potresti pensare che MySQL valuterà prima @a e poi farà un compito secondo: SELEZIONA @a, @a: = @ a + 1, ...; Tuttavia, l'ordine di valutazione per le espressioni che coinvolgono le variabili utente non è definito. "
ReverseEMF

3
@ReverseEMF: No. L'ordine di assegnazione è fissato nelle espressioni MySQL. Da quanto hai citato, la documentazione di MySQL suggerisce di non utilizzare più variabili in modo indipendente. Nel caso sopra, la valutazione dell'espressione è destinata ad avvenire in un ordine predefinito a causa di una singola espressione di assegnazione `` .id utenti` = @count: = @count + 1`. Dalla documentazione: "Il valore sul lato destro può essere un valore letterale, un'altra variabile che memorizza un valore o qualsiasi espressione legale che produce un valore scalare"
Anshul

1
È un'affermazione molto costosa? Come si comporterebbe in una tabella multi gigabyte? Ho paura di far saltare in aria il mio ibdata1 (transazione lunga) e di bloccare il tavolo per troppo tempo.
Stefan,

1
@Stefan Su una tabella (5 MB) con chiavi esterne che fa riferimento a un'altra con + 2 GB di dati, questo script non ha richiesto più di cinque minuti. Il sistema ha un SSD, quindi suppongo che abbia aiutato molto. Il fk ebbe il CASO AGGIORNATO
fernandezr il

60

Per ripristinare gli ID della mia tabella utente, utilizzo la seguente query SQL. È stato detto sopra che questo rovinerà qualsiasi relazione che potresti avere con qualsiasi altra tabella.

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;

Su una tabella MyISAM con 584k righe, sono stati necessari circa 7,3 secondi.
user1278519

3
Se avessi ottenuto un voto di 100 avrei votato tutto il tempo dato che rompe le dichiarazioni sql per un noob come me che aiuta a capire
repzero

1
La seconda riga non è necessaria o sbaglio? Comincia da 1 da solo ,,,, per InnoDB lo ha fatto per me
Thielicious

seconda riga non necessaria.
KawaiKx,

1
@ JorgeAugustoMorêradeMoura l'ordine dei record non verrà modificato
Ryan,

31

Puoi semplicemente usare questa query

alter table abc auto_increment = 1;

2
Questo non funzionerà in questo caso. Per le tabelle ISAM, imposterà il valore di autoinc su max (id) + 1. Per InnoDB non farà nulla. Consulta i documenti della tabella delle modifiche per modificare AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder

3
@Ireeder dal 5.6 in poi il comportamento per innodb è simile a quello del myisam
Anshul,

Se hai altre tabelle che hanno chiavi esterne a questa tabella, questo le romperà?
Kyle Vassella,

15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Penso che questo lo farà


12

Oppure, da PhpMyAdmin, rimuovere il flag "AutoIncrement", salvarlo, impostarlo di nuovo e salvare. Questo lo resetta.


Spiacenti, non posso eseguire il test con le versioni phpmyadmin correnti. La mia risposta è piuttosto vecchia ... Se mi hai votato per favore, puoi modificarlo?
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;

se lo desidera order by


1

in phpmyadmin

nota: questo funzionerà se si eliminano le ultime righe e non quelle centrali.

vai alla tua tabella-> fai clic sul menu delle operazioni-> vai alle opzioni della tabella-> cambia AUTO_INCREMENT a quel no da dove vuoi iniziare.

il tuo incremento automatico del tavolo inizia da quel n.

Provalo. inserisci qui la descrizione dell'immagine


0

Puoi rimuovere la funzionalità di incremento automatico della chiave primaria di quella colonna, quindi ogni volta che aggiorni quella colonna esegui prima una query che conterà tutte le righe nella tabella, quindi esegui un ciclo che scorre attraverso quel conteggio di righe inserendo ogni valore nel rispettiva riga e infine eseguire una query inserendo una nuova riga con il valore di quella colonna pari al conteggio totale delle righe più uno. Funzionerà perfettamente ed è la soluzione più assoluta per qualcuno che cerca di realizzare ciò che sei. Ecco un esempio di codice che è possibile utilizzare per la funzione:

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

Qui ho creato un array associativo che avevo aggiunto su una colonna di rango con la query all'interno di una query selezionata, che dava a ciascuna riga un valore di rango che iniziava con 1. Ho quindi ripetuto l'array associativo.

Un'altra opzione sarebbe stata quella di ottenere il conteggio delle righe, eseguire una query di selezione di base, ottenere l'array associativo e iterarlo allo stesso modo, ma con una variabile aggiunta che si aggiorna attraverso ogni iterazione. Questo è meno flessibile ma realizzerà la stessa cosa.

$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

per InnoDB, fai questo (questo rimuoverà tutti i record da una tabella, prima creerà un 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

Avevo gli stessi dubbi, ma non ho potuto apportare modifiche al tavolo, ho deciso di fare quanto segue visto che il mio ID non ha superato il numero massimo impostato nella variabile @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 soluzione richiede, ma è sicura ed era necessaria perché la mia tabella possedeva chiavi esterne con dati in un'altra tabella.


0

La scelta migliore è modificare la colonna e rimuovere l'attributo auto_increment. Quindi rilascia un'altra istruzione alter e rimetti auto_increment sulla colonna. Ciò reimposterà il conteggio al massimo + 1 delle righe correnti e quindi preserverà i riferimenti di chiave esterna a questa tabella, da altre tabelle nel database o da qualsiasi altro utilizzo della chiave per quella colonna.


0

La mia opinione è quella di creare una nuova colonna chiamata row_order. quindi riordinare quella colonna. Non sto accettando le modifiche alla chiave primaria. Ad esempio, se la colonna dell'ordine è banner_position, ho fatto qualcosa del genere, questo è per eliminare, aggiornare, creare la colonna di posizione del banner. Chiamare questa funzione riordinarli rispettivamente.

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

Funziona - https ://stackoverflow.com/a/5437720/10219008..... ma se si verifica un problema "Codice errore: 1265. Dati troncati per la colonna" id "nella riga 1" ... Quindi eseguire il seguente. L'aggiunta di ignora sulla query di aggiornamento.

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

-2

È anche possibile semplicemente evitare di utilizzare ID numerici come chiave primaria. È possibile utilizzare i codici Paese come ID principale se la tabella contiene informazioni sui paesi, oppure è possibile utilizzare permalink, ad esempio se contiene articoli.

Puoi anche semplicemente usare un valore casuale o MD5. Tutte queste opzioni hanno i suoi vantaggi, specialmente in ambito IT sec. gli ID numerici sono facili da enumerare.


1
... Su cosa basi questo? Forse non creare pagine come "complete_user_info_export.php? Userid = 34"? Internamente utilizzando una stringa o altro valore casuale come indice / identificatore è una davvero pessima idea. Crea più problemi di quanti ne risolva (se risolve anche qualsiasi problema)
Rob

Il valore MD5 è la peggiore possibilità assoluta perché è possibile che due valori o set di dati diversi producano lo stesso valore MD5. Quindi non la definirei una soluzione.
David,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.