Изменить порядок / сбросить первичный ключ с автоматическим увеличением


127

У меня есть таблица MySQL с первичным ключом автоматического увеличения. Я удалил несколько строк в середине таблицы. Теперь у меня есть, например, что-то вроде этого в столбце ID: 12, 13, 14, 19, 20. Я удалил 15, 16, 17 и 18 строк.

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

Как мне это сделать?

Ответы:


95

Вы можете отбросить столбец первичного ключа и воссоздать его. Затем следует переназначить все идентификаторы по порядку.

Однако в большинстве ситуаций это, вероятно, плохая идея. Если у вас есть другие таблицы, у которых есть внешние ключи к этой таблице, то это точно не сработает.


У меня есть другие таблицы, которые содержат внешний ключ к этой таблице, но я только начинаю проект, так что для меня все в порядке .. Спасибо!
Джонатан

65
Лучше всего в долгосрочной перспективе, если вы попытаетесь принять тот факт, что ваши идентификаторы не всегда будут последовательными, иначе, когда вы начнете работать над более крупными проектами, это действительно сведет с ума!
Киаран МакНалти,

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 не советует этого: «Как правило, кроме операторов SET, вы никогда не должны присваивать значение пользовательской переменной и читать значение в том же операторе. Например, для увеличения переменной это нормально: SET @a = @a + 1; Для других операторов, таких как SELECT, вы можете получить ожидаемые результаты, но это не гарантируется. В следующем операторе вы можете подумать, что MySQL сначала оценит @a, а затем выполнит присваивание второй: SELECT @a, @a: = @ a + 1, ...; Однако порядок оценки для выражений, включающих пользовательские переменные, не определен. "
ReverseEMF

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

1
Это очень дорогое заявление? Как он будет работать в многогигабайтной таблице? Я боюсь взорвать мою ibdata1 (длинную транзакцию) и заблокировать таблицу слишком долго.
Стефан

1
@Stefan Для таблицы (5 МБ) с внешними ключами, которая ссылается на другую с + 2 ГБ данных, этот сценарий не занял более пяти минут. В системе есть ssd, так что я полагаю, что это очень помогло. Ф.К. имел ON UPDATE CASCADE
fernandezr

60

Чтобы сбросить идентификаторы моей таблицы User, я использую следующий 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 с 584 тыс. Строк это заняло около 7,3 секунды.
user1278519

3
Если бы у меня было 100 голосов, я бы голосовал за это все время, так как он разбивает SQL-операторы для такого новичка, как я, которые помогают в понимании
repzero

1
Вторая строка не нужна или я ошибаюсь? Он начинается с 1 сам по себе ,,,, для InnoDB это
сработало

вторая строка не нужна.
KawaiKx

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

31

Вы можете просто использовать этот запрос

alter table abc auto_increment = 1;

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

3
@Ireeder, начиная с версии 5.6, поведение innodb схоже с поведением myisam
Аншул

Если у вас есть другие таблицы, у которых есть внешние ключи к этой таблице, сломает ли это их?
Кайл Васселла

15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Я думаю, это сработает


12

Или из PhpMyAdmin удалите флаг «AutoIncrement», сохраните, установите его снова и сохраните. Это сбрасывает его.


Извините, я не могу протестировать текущие версии 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. затем измените порядок столбца. Я не принимаю изменения первичного ключа. В качестве примера, если столбец заказа - banner_position, я сделал что-то вроде этого: это для удаления, обновления, создания столбца позиции баннера. Вызовите эту функцию, измените их порядок.

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. У всех этих возможностей есть свои преимущества, особенно в IT-сфере. числовые идентификаторы легко перечислить.


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

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