Reordenar / restablecer clave primaria de incremento automático

127

Tengo una tabla MySQL con una clave primaria de incremento automático. Eliminé algunas filas en el medio de la tabla. Ahora tengo, por ejemplo, algo como esto en la columna ID: 12, 13, 14, 19, 20. Eliminé las filas 15, 16, 17 y 18.

Quiero reasignar / restablecer / reordenar la clave principal para tener continuidad, es decir, hacer 19 a 15, 20 a 16, y así sucesivamente.

¿Cómo puedo hacerlo?

Jonathan
fuente

Respuestas:

95

Puede soltar la columna de clave principal y volver a crearla. Todos los identificadores deben reasignarse en orden.

Sin embargo, esta es probablemente una mala idea en la mayoría de las situaciones. Si tiene otras tablas que tienen claves foráneas para esta tabla, definitivamente no funcionará.

Tom Haigh
fuente
Tengo otras tablas que tienen una clave externa para esta tabla, pero recién estoy comenzando el proyecto, así que está bien para mí. ¡Gracias!
Jonathan
65
Puede ser mejor a largo plazo si intenta y comienza a aceptar que sus ID no siempre serán secuenciales, de lo contrario, cuando comience a trabajar en proyectos más grandes, ¡realmente se volverá loco!
Ciaran McNulty
8
ALTER TABLE your_table AUTO_INCREMENT = 1
Sinac
356

Aunque esta pregunta parece ser bastante antigua, publicará una respuesta para alguien que busque aquí.

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

Si la columna se usa como clave externa en otras tablas, asegúrese de usarla en ON UPDATE CASCADElugar de la predeterminada ON UPDATE NO ACTIONpara la relación de clave externa en esas tablas.

Además, para restablecer el AUTO_INCREMENTrecuento, puede emitir inmediatamente la siguiente declaración.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Para MySQL restablecerá el valor a MAX(id) + 1.

Anshul
fuente
Esto con claves foráneas es una solución genial para mi tabla donde se inserta y elimina mucha basura y quiero ahorrar espacio en el índice.
mukunda
1
mySQL Doc desaconseja esto: "Como regla general, aparte de las declaraciones SET, nunca debe asignar un valor a una variable de usuario y leer el valor dentro de la misma declaración. Por ejemplo, para incrementar una variable, esto está bien: SET @a = @a + 1; para otras declaraciones, como SELECT, puede obtener los resultados que espera, pero esto no está garantizado. En la siguiente declaración, puede pensar que MySQL evaluará @a primero y luego realizará una asignación segundo: SELECCIONE @a, @a: = @ a + 1, ...; Sin embargo, el orden de evaluación para las expresiones que involucran variables de usuario no está definido. "
ReverseEMF
3
@ReverseEMF: No. El orden de asignación se fija en expresiones MySQL. De lo que citó, la documentación de MySQL aconseja contra el uso independiente múltiple de la variable. En el caso anterior, la evaluación de la expresión está destinada a suceder en un orden predefinido debido a una sola expresión de asignación `` .id de usuario` = @count: = @count + 1`. De la documentación: "El valor en el lado derecho puede ser un valor literal, otra variable que almacene un valor o cualquier expresión legal que produzca un valor escalar"
Anshul
1
¿Es esta una declaración muy costosa? ¿Cómo funcionaría en una tabla de varios gigabytes? Tengo miedo de volar mi ibdata1 (transacción larga) y bloquear la mesa durante demasiado tiempo.
Stefan
1
@Stefan En una tabla (5MB) con claves foráneas que hace referencia a otra con + 2GB de datos, este script no tardó más de cinco minutos. El sistema tiene un SSD, así que supongo que ayudó mucho. El FK tuvo la ON UPDATE CASCADE
fernandezr
60

Para restablecer los ID de mi tabla de usuario, utilizo la siguiente consulta SQL. Se ha dicho anteriormente que esto arruinará cualquier relación que pueda tener con cualquier otra tabla.

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
fuente
En una tabla MyISAM con 584k filas, tardó aproximadamente 7.3 segundos.
user1278519
3
Si tuviera 100 votos, lo hubiera votado todo el tiempo, ya que desglosa las declaraciones de sql para un novato como yo que ayuda a la comprensión
repzero
1
La segunda línea no es necesaria o estoy equivocado? Comienza por 1 por sí mismo, para InnoDB lo hizo para mí
Thielicious
segunda línea no necesaria.
KawaiKx
1
@ JorgeAugustoMorêradeMoura el orden de los registros no se cambiará
Ryan
31

Simplemente puede usar esta consulta

alter table abc auto_increment = 1;
Aaron W.
fuente
2
Esto no funcionará en este caso. Para las tablas ISAM, establecerá el valor de autoinc en max (id) + 1. Para InnoDB no hará nada. Ver documentos alter table para cambiar AutoIncrement dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder
3
@Ireeder a partir del 5.6 el comportamiento para innodb es similar al de myisam
Anshul
Si tiene otras tablas que tienen claves foráneas para esta tabla, ¿las romperá?
Kyle Vassella
15
SET  @num := 0;

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

ALTER TABLE your_table AUTO_INCREMENT =1;

Creo que esto lo hará

bagyei37
fuente
12

O, desde PhpMyAdmin, elimine el indicador "AutoIncrement", guárdelo, configúrelo nuevamente y guárdelo. Esto lo restablece.

lbrutti
fuente
Lo siento, no puedo probar con las versiones actuales de phpmyadmin. Mi respuesta es bastante antigua ... Si me rechazaste, ¿puedes enmendarla?
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;

si quieres order by

Shubham Malik
fuente
1

en phpmyadmin

nota: esto funcionará si elimina las últimas filas, no las filas intermedias.

pase a su tabla-> haga clic en el menú de operaciones-> vaya a opciones de tabla-> cambie AUTO_INCREMENT a ese no desde donde desea comenzar.

su tabla de autoincremento comienza desde ese no.

intentalo. ingrese la descripción de la imagen aquí

Anjani Barnwal
fuente
0

Puede eliminar la funcionalidad de incremento automático de la clave primaria de esa columna, luego, cada vez que actualice esa columna, ejecute una consulta de antemano que contará todas las filas de la tabla, luego ejecute un bucle que recorre ese recuento de filas insertando cada valor en el fila respectiva, y finalmente ejecute una consulta insertando una nueva fila con el valor de esa columna como el recuento total de filas más uno. Esto funcionará perfectamente y es la solución más absoluta para alguien que intenta lograr lo que eres. Aquí hay un ejemplo de código que puede usar para la función:

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

Aquí creé una matriz asociativa que había agregado en una columna de rango con la consulta dentro de una consulta de selección, que le dio a cada fila un valor de rango que comienza con 1. Luego recorrí la matriz asociativa.

Otra opción hubiera sido obtener el recuento de filas, ejecutar una consulta de selección básica, obtener la matriz asociativa e iterarla de la misma manera pero con una variable adicional que se actualiza a través de cada iteración. Esto es menos flexible pero logrará lo mismo.

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

para InnoDB, haga esto (esto eliminará todos los registros de una tabla, primero haga 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 ;
Luis
fuente
0

Tenía las mismas dudas, pero no pude hacer ningún cambio en la tabla, decidí hacer lo siguiente al ver que mi ID no excedía el número máximo establecido en la variable @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 solución toma, pero es segura y era necesaria porque mi tabla poseía claves externas con datos en otra tabla.

Rodrigo Prazim
fuente
0

La mejor opción es alterar la columna y eliminar el atributo auto_increment. Luego emita otra instrucción alter y vuelva a colocar auto_increment en la columna. Esto restablecerá el recuento al máximo + 1 de las filas actuales y, por lo tanto, conservará las referencias de clave externa a esta tabla, desde otras tablas de su base de datos o cualquier otro uso de clave para esa columna.

Grwww
fuente
0

Mi opinión es crear una nueva columna llamada row_order. luego reordenar esa columna. No acepto los cambios a la clave primaria. Como ejemplo, si la columna de orden es banner_position, he hecho algo como esto: Esto es para eliminar, actualizar, crear una columna de posición de banner. Llame a esta función reordenarlos 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
fuente
0

Esto funciona: https ://stackoverflow.com/a/5437720/10219008..... pero si se encuentra con un problema 'Código de error: 1265. Datos truncados para la columna' id 'en la fila 1' ... Luego ejecute el seguimiento. Agregar ignorar en la consulta de actualización.

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

También puede evitar simplemente usar ID numéricos como clave principal. Puede usar códigos de país como identificación principal si la tabla contiene información de países, o puede usar enlaces permanentes, por ejemplo, si contiene artículos.

También podría simplemente usar un valor aleatorio o MD5. Todas estas opciones tienen sus propios beneficios, especialmente en TI sec. Las identificaciones numéricas son fáciles de enumerar.

Chris Russo
fuente
1
... ¿En qué basas esto? ¿Quizás simplemente no haga páginas como "complete_user_info_export.php? Userid = 34"? Usar internamente una cadena u otro valor aleatorio como índice / identificador es una muy mala idea. Crea más problemas de los que resuelve (si incluso resuelve algún problema)
Rob
El valor MD5 es la peor posibilidad absoluta porque es posible que dos valores o conjuntos de datos diferentes produzcan el mismo valor MD5. Entonces no lo llamaría una solución.
David