Zmień kolejność / zresetuj klucz podstawowy z automatycznym zwiększaniem wartości


127

Mam tabelę MySQL z kluczem podstawowym o automatycznym zwiększaniu. Usunąłem kilka wierszy na środku tabeli. Teraz mam na przykład coś takiego w kolumnie ID: 12, 13, 14, 19, 20. Usunąłem 15, 16, 17 i 18 wierszy.

Chcę zmienić przypisanie / zresetować / zmienić kolejność klucza podstawowego, aby uzyskać ciągłość, tj. Ustawić 19 na 15, 20 na 16 i tak dalej.

Jak mogę to zrobić?

Odpowiedzi:


95

Możesz usunąć kolumnę klucza podstawowego i ponownie ją utworzyć. Następnie należy ponownie przypisać wszystkie identyfikatory w kolejności.

Jednak w większości sytuacji jest to prawdopodobnie zły pomysł. Jeśli masz inne tabele, które mają klucze obce do tej tabeli, to na pewno nie zadziała.


Mam inne tabele, które zawierają obcy klucz do tej tabeli, ale dopiero zaczynam projekt, więc wszystko jest w porządku. Dzięki!
Jonathan

65
Najlepiej byłoby na dłuższą metę, jeśli spróbujesz zaakceptować fakt, że twoje identyfikatory nie zawsze będą sekwencyjne, w przeciwnym razie, gdy zaczniesz pracować nad większymi projektami, naprawdę doprowadzi to do szaleństwa!
Ciaran McNulty

8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac

356

Nawet jeśli to pytanie wydaje się dość stare, zamieści odpowiedź dla kogoś, kto sięgnie tutaj w poszukiwaniu.

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

Jeśli kolumna jest używana jako klucz obcy w innych tabelach, upewnij się, że używasz ON UPDATE CASCADEzamiast wartości domyślnej ON UPDATE NO ACTIONrelacji klucza obcego w tych tabelach.

Ponadto, aby zresetować AUTO_INCREMENTlicznik, możesz natychmiast wydać następujące oświadczenie.

ALTER TABLE `users` AUTO_INCREMENT = 1;

W przypadku MySQL zresetuje wartość do MAX(id) + 1.


To z obcymi kluczami jest bardzo fajnym rozwiązaniem dla mojej tabeli, w której wiele śmieci jest wstawianych i usuwanych, a ja chcę zaoszczędzić miejsce na indeks.
mukunda

1
MySQL Doc odradza to: „Zgodnie z ogólną zasadą, inną niż w instrukcjach SET, nigdy nie należy przypisywać wartości do zmiennej użytkownika i czytać wartości w tej samej instrukcji. Na przykład, aby zwiększyć wartość zmiennej, jest to w porządku: SET @a = @a + 1; W przypadku innych instrukcji, takich jak SELECT, możesz uzyskać oczekiwane wyniki, ale nie jest to gwarantowane. W poniższej instrukcji możesz pomyśleć, że MySQL najpierw oceni @a, a następnie wykona przypisanie sekunda: SELECT @a, @a: = @ a + 1, ...; Jednak kolejność oceny wyrażeń obejmujących zmienne użytkownika jest niezdefiniowana. ”
OdwróćEMF

3
@ReverseEMF: Nie. Kolejność przypisywania jest ustalona w wyrażeniach MySQL. Z tego, co zacytowałeś, dokumentacja MySQL zawiera porady dotyczące wielokrotnego niezależnego używania zmiennej. W powyższym przypadku ocena wyrażenia musi się odbyć w predefiniowanej kolejności z powodu pojedynczego wyrażenia przypisania `` .id użytkownika` = @count: = @count + 1`. Z dokumentacji: „Wartość po prawej stronie może być wartością literalną, inną zmienną przechowującą wartość lub jakimkolwiek wyrażeniem prawnym, które daje wartość skalarną”
Anshul

1
Czy to bardzo kosztowne zestawienie? Jak by to działało w tabeli o wielu gigabajtach? Boję się wysadzenia mojego ibdata1 (długa transakcja) i zbyt długiego zablokowania stołu.
Stefan

1
@Stefan Na stole (5 MB) z kluczami obcymi, który odwołuje się do innego z + 2 GB danych, ten skrypt nie zajął więcej niż pięć minut. System ma SSD, więc przypuszczam, że to bardzo pomogło. Fk miał ON UPDATE CASCADE
fernandezr

60

Aby zresetować identyfikatory mojej tabeli użytkowników, używam następującego zapytania SQL. Powiedziano powyżej, że zrujnuje to wszelkie relacje, które możesz mieć z innymi stołami.

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;

Na tabeli MyISAM z 584 tysiącami wierszy zajęło to około 7,3 sekundy.
user1278519

3
Gdybym miał 100 głosów, głosowałbym za tym przez cały czas, ponieważ łamie on oświadczenia sql dla nooba takiego jak ja, co pomaga w zrozumieniu
repzero

1
Druga linia nie jest konieczna, czy się mylę? Zaczyna się samodzielnie od 1
,,,

druga linia nie jest konieczna.
KawaiKx

1
@ JorgeAugustoMorêradeMoura kolejność rekordów nie ulegnie zmianie
Ryan

31

Możesz po prostu użyć tego zapytania

alter table abc auto_increment = 1;

2
W tym przypadku to nie zadziała. Dla tabel ISAM ustawi wartość autoinc na max (id) + 1. Dla InnoDB nic nie zrobi. Zobacz dokumentację alter table dotyczącą zmiany AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder

3
@Ireeder od wersji 5.6 i nowszej zachowanie innodb jest podobne do zachowania myisam
Anshul

Jeśli masz inne tabele, które mają klucze obce do tej tabeli, czy spowoduje to ich uszkodzenie?
Kyle Vassella

15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Myślę, że to wystarczy


12

Lub w PhpMyAdmin usuń flagę „AutoIncrement”, zapisz, ustaw ponownie i zapisz. To zresetuje ją.


Przepraszamy, nie mogę przetestować aktualnych wersji phpmyadmin. Moja odpowiedź jest dość stara ... Jeśli mnie nie oddałeś, czy możesz to zmienić?
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;

Jeśli chcesz order by


1

w phpmyadmin

Uwaga: zadziała, jeśli usuniesz ostatnie wiersze, a nie środkowe wiersze.

goto your table-> kliknij menu operacyjne-> goto table options-> zmień AUTO_INCREMENT na to no, od którego chcesz zacząć.

Twój automatyczny przyrost tabeli zacznij od tego nie.

Spróbuj. wprowadź opis obrazu tutaj


0

Możesz usunąć funkcję automatycznego zwiększania wartości klucza podstawowego tej kolumny, a następnie za każdym razem, gdy aktualizujesz tę kolumnę, uruchom zapytanie, które policzy wszystkie wiersze w tabeli, a następnie uruchom pętlę, która iteruje przez tę liczbę wierszy, wstawiając każdą wartość do odpowiedni wiersz i na koniec uruchom zapytanie, wstawiając nowy wiersz z wartością w tej kolumnie będącą całkowitą liczbą wierszy plus jeden. To zadziała bezbłędnie i jest najbardziej absolutnym rozwiązaniem dla kogoś, kto próbuje osiągnąć to, kim jesteś. Oto przykład kodu, którego możesz użyć dla funkcji:

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

Tutaj utworzyłem tablicę asocjacyjną, którą dołączyłem do kolumny rang z zapytaniem w zapytaniu wybierającym, co dało każdemu wierszowi wartość rangi zaczynającą się od 1. Następnie wykonałem iterację po tablicy asocjacyjnej.

Inną opcją byłoby pobranie liczby wierszy, uruchomienie podstawowego zapytania wybierającego, pobranie tablicy asocjacyjnej i wykonanie iteracji w ten sam sposób, ale z dodaną zmienną, która aktualizuje się w każdej iteracji. Jest to mniej elastyczne, ale da to samo.

$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

w przypadku InnoDB zrób to (spowoduje to usunięcie wszystkich rekordów z tabeli, najpierw wykonaj 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

Miałem te same wątpliwości, ale nie mogłem dokonać żadnych zmian w tabeli, zdecydowałem, że wykonując następujące czynności, widząc, że mój identyfikator nie przekracza maksymalnej liczby ustawionej w zmiennej @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;

Rozwiązanie wymaga, ale jest bezpieczne i było konieczne, ponieważ moja tabela posiadała klucze obce z danymi w innej tabeli.


0

Najlepszym wyborem jest zmiana kolumny i usunięcie atrybutu auto_increment. Następnie wydaj kolejną instrukcję alter i umieść auto_increment z powrotem w kolumnie. Spowoduje to zresetowanie liczby do maks. + 1 bieżących wierszy, a tym samym zachowanie odwołań do kluczy obcych z powrotem do tej tabeli, z innych tabel w bazie danych lub dowolnego innego użycia klucza dla tej kolumny.


0

Moim zdaniem jest utworzenie nowej kolumny o nazwie row_order. następnie zmień kolejność w tej kolumnie. Nie akceptuję zmian w kluczu podstawowym. Na przykład, jeśli kolumna zamówienia to pozycja_bannera, zrobiłem coś takiego: To jest do usuwania, aktualizacji, tworzenia kolumny pozycji banera. Wywołaj tę funkcję, odpowiednio je uporządkuj.

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

To działa - https ://stackoverflow.com/a/5437720/10219008..... ale jeśli napotkasz problem „Kod błędu: 1265. Dane zostały obcięte dla kolumny„ id ”w wierszu 1” ... Następnie uruchom następujące. Dodanie ignorowania zapytania aktualizującego.

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

-2

Możesz także po prostu uniknąć używania identyfikatorów numerycznych jako klucza podstawowego. Możesz użyć kodów krajów jako podstawowego identyfikatora, jeśli tabela zawiera informacje o krajach, lub możesz użyć linków bezpośrednich, jeśli na przykład zawiera artykuły.

Możesz też po prostu użyć wartości losowej lub MD5. Wszystkie te opcje mają swoje zalety, szczególnie w zakresie IT. Identyfikatory numeryczne są łatwe do wyliczenia.


1
... Na czym to opierasz? Może po prostu nie twórz stron takich jak „complete_user_info_export.php? Userid = 34”? Wewnętrzne użycie łańcucha lub innej losowej wartości jako indeksu / identyfikatora jest naprawdę złym pomysłem. To stwarza więcej problemów niż rozwiązuje (jeśli w ogóle rozwiązuje jakiekolwiek problemy)
Rob

Wartość MD5 jest absolutnie najgorszą możliwością, ponieważ jest możliwe, że dwie różne wartości lub zbiory danych dają tę samą wartość MD5. Więc nie nazwałbym tego rozwiązaniem.
David
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.