Reordenar / redefinir chave primária de incremento automático

127

Eu tenho uma tabela MySQL com uma chave primária de incremento automático. Eu apaguei algumas linhas no meio da tabela. Agora, por exemplo, tenho algo assim na coluna ID: 12, 13, 14, 19, 20. Excluí as 15, 16, 17 e 18 linhas.

Quero reatribuir / redefinir / reordenar a chave primária para ter continuidade, ou seja, fazer 19 a 15, 20 a 16, e assim por diante.

Como eu posso fazer isso?

Jonathan
fonte

Respostas:

95

Você pode soltar a coluna da chave primária e recriá-la. Todos os IDs devem ser reatribuídos em ordem.

No entanto, esta é provavelmente uma má ideia na maioria das situações. Se você tiver outras tabelas que tenham chaves estrangeiras para essa tabela, ela definitivamente não funcionará.

Tom Haigh
fonte
Eu tenho outras tabelas que contêm uma chave estrangeira para essa tabela, mas estou apenas começando o projeto, então está tudo bem para mim .. Obrigado!
Jonathan
65
Pode ser o melhor a longo prazo, se você tentar aceitar que seus IDs nem sempre serão seqüenciais; caso contrário, quando você começar a trabalhar em projetos maiores, isso ficará realmente louco!
Ciaran McNulty
8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac
356

Mesmo que essa pergunta pareça bastante antiga, postará uma resposta para alguém que chegar aqui pesquisando.

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

Se a coluna for usada como chave estrangeira em outras tabelas, certifique-se de usar em ON UPDATE CASCADEvez do padrão ON UPDATE NO ACTIONpara o relacionamento de chave estrangeira nessas tabelas.

Além disso, para redefinir a AUTO_INCREMENTcontagem, você pode emitir imediatamente a seguinte declaração.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Para MySQLs, ele redefinirá o valor para MAX(id) + 1.

Anshul
fonte
Isso com chaves estrangeiras é uma solução muito legal para a minha tabela, onde um monte de lixo é inserido e excluído e eu quero economizar espaço de índice.
mukunda
1
O documento do mySQL desaconselha o seguinte: "Como regra geral, exceto nas instruções SET, você nunca deve atribuir um valor a uma variável do usuário e ler o valor na mesma instrução. Por exemplo, para incrementar uma variável, tudo bem: SET @a = @a + 1; Para outras instruções, como SELECT, você pode obter os resultados esperados, mas isso não é garantido.Na declaração a seguir, você pode pensar que o MySQL avaliará @a primeiro e depois fará uma atribuição segundo: SELECT @a, @a: = @ a + 1, ...; No entanto, a ordem de avaliação para expressões que envolvem variáveis ​​de usuário é indefinida. "
ReverseEMF
3
@ReverseEMF: Não. A ordem de atribuição é fixada nas expressões MySQL. Pelo que você citou, a documentação do MySQL desaconselha o uso independente e múltiplo da variável. No caso acima, a avaliação da expressão é obrigada a ocorrer em uma ordem predefinida devido a uma única expressão de atribuição `` users .id` = @count: = @count + 1`. Na documentação: "O valor no lado direito pode ser um valor literal, outra variável que armazena um valor ou qualquer expressão legal que produza um valor escalar"
Anshul
1
Esta é uma afirmação muito cara? Como ele funcionaria em uma tabela de vários gigabytes? Tenho medo de explodir meu ibdata1 (transação longa) e bloquear a tabela por muito tempo.
Stefan
1
@ Stefan Em uma tabela (5 MB) com chaves estrangeiras que faz referência a outra com + 2 GB de dados, esse script não levou mais de cinco minutos. O sistema tem um ssd, então suponho que ajudou muito. O fk teve o ON UPDATE CASCADE
fernandezr
60

Para redefinir os IDs da minha tabela de usuário, eu uso a seguinte consulta SQL. Foi dito acima que isso arruinará qualquer relacionamento que você possa ter com outras tabelas.

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
fonte
Em uma tabela MyISAM com 584k linhas, demorou cerca de 7,3 segundos.
User1278519
3
Se eu tivesse 100 votos, eu teria votado nisso o tempo todo, uma vez que ele divide as instruções sql de um noob como eu, que ajuda no entendimento.
repzero
1
A segunda linha não é necessária ou estou errado? Começa por 1 em sua própria ,,,, para InnoDB ele fez para me embora
Thielicious
segunda linha não é necessária.
KawaiKx
1
@ JorgeAugustoMorêradeMoura a ordem dos registros não será alterada
Ryan
31

Você pode simplesmente usar esta consulta

alter table abc auto_increment = 1;
Aaron W.
fonte
2
Isso não vai funcionar neste caso. Para tabelas ISAM, ele definirá o valor autoinc como max (id) + 1. Para o InnoDB, ele não fará nada. Consulte alterar os documentos da tabela para alterar o AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder
3
@Ireeder de 5,6 em diante comportamento para innodb é semelhante ao do myisam
Anshul
Se você tiver outras tabelas que possuem chaves estrangeiras para esta tabela, isso será interrompido?
Kyle Vassella
15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Eu acho que isso fará isso

bagyei37
fonte
12

Ou, no PhpMyAdmin, remova o sinalizador "AutoIncrement", salve, defina-o novamente e salve.

lbrutti
fonte
Desculpe, não posso testar com as versões atuais do phpmyadmin. Minha resposta é bastante antiga ... Se você me recusou, pode emendá-la?
Lbrutti 26/11/19
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 você quiser order by

Shubham Malik
fonte
1

em phpmyadmin

Nota: isso funcionará se você excluir as últimas linhas e não as linhas do meio.

vá para sua tabela-> clique no menu de operações-> vá para opções de tabela-> altere AUTO_INCREMENT para aquele no de onde você deseja iniciar.

seu incremento automático de tabela começa com isso não.

tente. insira a descrição da imagem aqui

Anjani Barnwal
fonte
0

Você pode remover a funcionalidade de incremento automático da chave primária dessa coluna e, sempre que atualizar essa coluna, execute uma consulta com antecedência que contará todas as linhas da tabela e, em seguida, execute um loop que itera através dessa contagem de linhas, inserindo cada valor no respectiva linha e, finalmente, execute uma consulta inserindo uma nova linha com o valor dessa coluna sendo a contagem total de linhas mais uma. Isso funcionará perfeitamente e é a solução mais absoluta para alguém tentando realizar o que você é. Aqui está um exemplo de código que você pode usar para a função:

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

Aqui, criei uma matriz associativa que havia anexado a uma coluna de classificação com a consulta em uma consulta de seleção, que dava a cada linha um valor de classificação começando com 1. Depois, iterava pela matriz associativa.

Outra opção seria obter a contagem de linhas, executar uma consulta de seleção básica, obter a matriz associativa e iterá-la da mesma maneira, mas com uma variável adicionada que é atualizada a cada iteração. Isso é menos flexível, mas realizará a mesma coisa.

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

para o InnoDB, faça isso (isso removerá todos os registros de uma tabela, faça uma bakcup primeiro):

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

Eu tinha as mesmas dúvidas, mas não consegui fazer nenhuma alteração na tabela, decidi fazer o seguinte, visto que meu ID não excedia o número máximo definido na variável @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;

A solução leva, mas é segura e era necessária porque minha tabela possuía chaves estrangeiras com dados em outra tabela.

Rodrigo Prazim
fonte
0

A melhor opção é alterar a coluna e remover o atributo auto_increment. Em seguida, emita outra instrução alter e coloque auto_increment novamente na coluna. Isso redefinirá a contagem para o máximo + 1 das linhas atuais e, assim, preservará as referências de chave estrangeira de volta a esta tabela, de outras tabelas no banco de dados ou de qualquer outro uso de chave para essa coluna.

Grwww
fonte
0

Minha opinião é criar uma nova coluna chamada row_order. reordene essa coluna. Não estou aceitando as alterações na chave primária. Como exemplo, se a coluna da ordem for banner_position, eu fiz algo parecido com isto: Isto é para excluir, atualizar e criar a coluna de posição do banner. Chame esta função, reordene-os respectivamente.

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

Isso funciona - https://stackoverflow.com/a/5437720/10219008.....mas, se você tiver um problema 'Código de erro: 1265. Dados truncados para a coluna' id 'na linha 1' ... Em seguida, execute Os seguintes. Adicionando ignorar na consulta de atualização.

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

Você também pode simplesmente evitar o uso de IDs numéricos como Chave Primária. Você pode usar os códigos de país como identificação principal se a tabela contiver informações sobre os países ou os links permanentes, se contiver artigos, por exemplo.

Você também pode simplesmente usar um valor aleatório ou MD5. Todas essas opções têm seus próprios benefícios, especialmente no setor de TI. IDs numéricos são fáceis de enumerar.

Chris Russo
fonte
1
... Você baseia isto em quê? Talvez apenas não crie páginas como "complete_user_info_export.php? Userid = 34"? Internamente usando uma corda ou outro valor aleatório como index / identificador é uma muito má ideia. Ele cria mais problemas do que resolve (se ele mesmo resolve todos os problemas)
Rob
O valor MD5 é a pior possibilidade absoluta, pois é possível que dois valores ou conjuntos de dados diferentes produzam o mesmo valor MD5. Então, eu não chamaria isso de solução.
David