Перередагувати / скинути первинний ключ автоматичного збільшення


127

У мене є таблиця MySQL з первинним ключем автоматичного збільшення. Я видалив кілька рядків посеред таблиці. Зараз у мене є, наприклад, щось подібне у стовпчику ID: 12, 13, 14, 19, 20. Я видалив рядки 15, 16, 17 та 18.

Я хочу перепризначити / скинути / змінити порядок первинного ключа, щоб мати безперервність, тобто зробити 19 a 15, 20 a 16 тощо.

Як я можу це зробити?

Відповіді:


95

Можна скинути стовпчик первинного ключа та створити його знову. Після цього всі ідентифікатори повинні бути перекомпоновані в порядку.

Однак це, мабуть, погана ідея в більшості ситуацій. Якщо у вас є інші таблиці, у яких є сторонні ключі до цієї таблиці, вона точно не працюватиме.


У мене є інші таблиці, які містять зовнішній ключ до цієї таблиці, але я тільки починаю проект, так що для мене це нормально. Дякую!
Джонатан

65
Це може бути найкращим довгостроковим терміном, якщо ви спробуєте почати визнавати, що ваші посвідчення особи не завжди будуть послідовними, інакше, коли ви почнете працювати над великими проектами, це дійсно зведе з розуму!
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

1
mySQL Doc радить щодо цього: "Як правило, крім випадків у операторах SET, ви ніколи не повинні присвоювати значення змінній користувачеві та читати значення в межах одного виразу. Наприклад, для збільшення змінної це нормально: SET @a = @a + 1; для інших висловлювань, таких як SELECT, ви можете отримати очікувані результати, але це не гарантується. У наступному твердженні ви можете подумати, що MySQL спочатку оцінить @a, а потім виконає завдання друге: SELECT @a, @a: = @ a + 1, ...; Однак порядок оцінювання виразів, що містять змінні користувача, не визначено. "
ЗворотнійЕМФ

3
@ReverseEMF: Ні. Порядок призначення закріплений у виразах MySQL. З того, що ви цитували, документація MySQL радить проти багаторазового незалежного використання змінної. У вищенаведеному випадку оцінка виразу має бути визначена у визначеному порядку через вираз одного призначення `` users .id` = @count: = @count + 1`. З документації: "Значення з правого боку може бути буквальним значенням, іншою змінною, що зберігає значення, або будь-яким юридичним виразом, який дає скалярне значення"
Аншул

1
Це дуже дороге твердження? Як би це було виконано у багатогігабайтній таблиці? Я боюся підірвати свою ibdata1 (довгу транзакцію) і занадто довго блокувати стіл.
Стефан

1
@Stefan На столі (5 Мб) із сторонніми ключами, на який посилається інший із + 2 ГБ даних, цей сценарій не зайняв більше п'яти хвилин. У системі є ssd, тому я гадаю, що вона дуже допомогла. У
fernandezr

60

Для скидання ідентифікаторів моєї таблиці користувачів я використовую наступний 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;

На таблиці MyISAM з 584k рядками пройшло приблизно 7,3 секунди.
користувач1278519

3
Якби у мене було 100 голосів, я би проголосував за це весь час, оскільки він розбиває твердження sql для noob, як я, який допомагає зрозуміти
repzero

1
Другий рядок не потрібний чи я помиляюся? Він починається на 1 самостійно ,,,, для InnoDB це зробив і для мене
Thielicious

другий рядок не потрібен.
KawaiKx

1
@ JorgeAugustoMorêradeMoura порядок записів не буде змінено
Райан

31

Ви можете просто використовувати цей запит

alter table abc auto_increment = 1;

2
У цьому випадку це не спрацює. Для таблиць ISAM він встановить значення autoinc на max (id) + 1. Для InnoDB він нічого не зробить. Див. Документи про зміни alt для зміни AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder

3
@Ireeder від 5,6 року поведінка для InnoDB подібно до того з MyISAM
Anshul

Якщо у вас є інші таблиці, які мають зовнішні ключі до цієї таблиці, то чи буде це порушувати їх?
Кайл Васселла

15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Я думаю, що це вдасться


12

Або з PhpMyAdmin видаліть прапор "AutoIncrement", збережіть, встановіть його знову і save.this скидає його.


На жаль, не вдається перевірити поточні версії 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

У мене були ті самі сумніви, але я не зміг внести жодних змін у таблицю, я вирішив зробити наступне, побачивши, що мій ідентифікатор не перевищує максимальну кількість, встановлену у змінній @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 назад на стовпець. Це скине кількість до максимуму + 1 поточних рядків і, таким чином, збереже посилання іноземних ключів назад до цієї таблиці, з інших таблиць у вашій базі даних або будь-якого іншого використання ключа для цього стовпця.


0

Моя думка - створити новий стовпець під назвою row_order. потім упорядкуйте цей стовпець. Я не приймаю зміни до первинного ключа. Наприклад, якщо стовпець замовлення - банер_позиції, я зробив щось подібне. Це для видалення, оновлення, створення стовпця положення банера. Виклик цієї функції переупорядкуйте їх відповідно.

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.....але якщо ви стикаєтеся з проблемою "Код помилки: 1265. Дані усікаються для стовпця" id "у рядку 1" ... Потім запустіть наступне. Додавання ігнору в запиті оновлення.

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

-2

Ви також можете просто уникати використання числових ідентифікаторів як основного ключа. Ви можете використовувати коди країн як основний ідентифікатор, якщо таблиця містить інформацію про країни, або ви можете використовувати посилання, якщо вони містять статті, наприклад.

Ви також можете просто використовувати випадкове або значення MD5. Усі ці варіанти мають свої переваги, особливо в ІТ-сек. числові ідентифікатори легко перелічити.


1
... На чому ви це базуєте? Можливо, просто не робите сторінки типу "complete_user_info_export.php? Userid = 34"? Внутрішнє використання рядка або іншого випадкового значення в якості індексу / ідентифікатора - це дійсно погана ідея. Це створює більше проблем, ніж вирішує (якщо це навіть вирішує якісь проблеми)
Роб

Значення MD5 - це найгірша абсолютна можливість, оскільки можливо, що два різних значення або набори даних виробляють одне і те ж значення MD5. Тому я б не називав це рішенням.
Девід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.