2012-06-18 10 views
5

Tengo dos tablas como tal;MySQL arreglando brechas de autoincrement en dos tablas

id_image foo bar 
1   3  5 
2   8  1 
3   17  88 
7   14  23 
8   12  9 


id_image bar foo 
1   2  3 
1   5  6 
2   18  11 
2   10  12 
3   8  21 
3   17  81 
7   29  50 
7   1  14 
8   10  26 
8   27  34 

hay un hueco en el id_image autoincremented en la primera tabla. En la segunda tabla, el id_image se refiere al id_image en la primera tabla, y hay dos de cada ID allí.

Aviso: Esta tabla es teórica. No tengo ni idea donde el espacio es exactamente, o si hay incluso huecos múltiples. Todo lo que sé es que el primer valor es 1 y el último valor es más alto que el recuento total de filas.

Ahora, me gustaría solucionar este problema.

Antes de decir que las lagunas no importan y si lo hacen, es un mal diseño de la base de datos, déjame decirte; Estoy de acuerdo con usted.

Sin embargo, con lo que estoy tratando es de un sistema de código abierto de terceros (irremediablemente de atrás hacia atrás) al que debo importar una gran cantidad de datos existentes que no tienen identificadores de referencias cruzadas en múltiples mesas. La única manera en que puedo asegurarme de que los mismos datos obtengan un ID coincidente en todas las tablas del sistema es ingresarlo secuencialmente, y eso significa que no puedo tener espacios vacíos.

Entonces, lo que ahora necesito hacer es;

  1. Fijar la brecha en la columna de la id_image en la primera tabla, por lo que el último valor coincide con el número de filas.
  2. Edite la columna id_image en la segunda tabla para que su valor corresponda a la misma fila que se corresponde antes de la corrección de hueco.

¿Cómo podría comenzar a hacer esto? Entiendo que esto podría estar fuera de las capacidades del lenguaje de consulta de MySQL, por lo que las respuestas de PHP también son aceptables. ¡Gracias! :)

+0

¿es el motor innodb? – Sebas

+0

Sí lo es. Sin embargo, la brecha fue causada por tener que volver a ingresar una parte de los datos, no por el error INSERT IGNORE. :) –

+0

¿Puedes bloquear la tabla y luego recorrerla con 'SELECT image_id FORM table ORDER BY image_id ASC' y encontrar las lagunas? –

Respuesta

1

La idea básica aquí es encontrar todos los huecos primero para determinar cuánto necesita disminuir cada identificación. Luego, debe iterar a través de ambas tablas y aplicar la disminución. (Usted tendrá que añadir: anfitrión, db,, pasan usuario, y los nombres de las tablas reales)

try { 
    $pdo = new PDO('mysql:host=HOST;dbname=DB', 'user', 'pass'); 

    $pdo->beginTransaction(); 

    // Iterate through all id's in the first table 
    $stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw Exception('No rows in table'); 
    } 

    $lastId = $id; 
    $gaps = array(); 

    // Find all the gaps 
    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     if($id != ($lastId + 1)) { 
      $gaps[] = $id; 
     } 

     $lastId = $id; 
    } 


    if(!isset($gaps[0])) { 
     throw new Exception('No gaps found'); 
    } 

    // For each gap, update the range from the last gap to that gap by subtracting 
    // the number of gaps there has been from the id 
    $lastGap = $gaps[0]; 

    for($i = 1; $i < count($gaps); $i++) { 
     $stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
     $stmt->execute(array(
      ':i' => $i, 
      ':lastGap' => $lastGap, 
      ':gap' => $gaps[$i] 
     )); 

     $stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
     $stmt->execute(array(
      ':i' => $i, 
      ':lastGap' => $lastGap, 
      ':gap' => $gaps[$i] 
     )); 

     $lastGap = $gaps[$i]; 
    } 

    // Finally, fix the gap between the last found gap and the end of the table 
    $stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
    $stmt->execute(array(
     ':i' => $i, 
     ':lastGap' => $lastGap, 
     ':gap' => $gaps[$i] 
    )); 

    $stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :lastId'); 
    $stmt->execute(array(
     ':i' => $i, 
     ':lastGap' => $lastGap, 
     ':lastId' => $lastId 
    )); 

    // Verify everything is correct 
    $stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw new Exception('No rows'); // Should never be thrown 
    } 

    $lastId = $id; 

    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     if($id != ($lastId + 1)) { 
      throw new Exception('There was an error between ids ' . $lastId . ' and '. $id); 
     } 

     $lastId = $id; 
    } 

    $stmt = $pdo->exec('SELECT image_id FROM TableTwo ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw new Exception('No rows in table two'); // Shouldn't hit this 
    } 

    $lastId = $id; 
    $ids = array($id); 

    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     $ids[] = $id; 

     if(count($ids) == 2) { 
      if($ids[0] !== $ids[1]) { 
       throw new Exception('Table two error on ids '); 
      } 

      if($ids[0] !== $lastId) { 
       throw new Exception('Table two error on id gapfix'); 
      } 

      $lastId = $ids[0]; 
      $ids = array(); 
     } 
    } 

    $pdo->commit(); 
} catch(Exception $e) { 
    $pdo->rollBack(); 

    var_dump($e); 
} 

Importante: Es posible que desee tirar esto en un archivo y ejecutar a través de la CLI: php -f gapfix.php e incluya una consulta antes del $pdo->commit() que devuelve una lista de todos los ID para que pueda verificar que la operación funcionó como se esperaba. Si no lo hizo, puede retroceder como si nada hubiera sucedido.El código ahora verifica si la primera tabla está en el orden correcto. Sin embargo, aún no verifica la segunda tabla.¡Todas las comprobaciones se han implementado!

+0

¿Está trabajando en ello también para verificar la segunda tabla? :) –

+0

@EmphramStavanger Agregándolo mientras hablamos ... –

+0

@EmphramStavanger ¡Implementado! –

3
ALTER TABLE table2 
ADD FOREIGN KEY FK_IMAGE (id_image) 
REFERENCES table1 (id_image) 
ON DELETE CASCADE 
ON UPDATE CASCADE; 

SET @currentRow = 0; 

UPDATE table1 INNER JOIN (
    SELECT @currentRow := @currentRow + 1 AS id_image_new, id_image AS id_image_old 
    FROM table1 
    ORDER BY id_image ASC) t on t.id_image_old = table1.id_image 
SET table1.id_image = t.id_image_new; 

ALTER TABLE table1 AUTO_INCREMENT = 1; 

El FK automáticamente actualizará los identificadores de su 2da mesa.

No estoy seguro en absoluto, pero en algunas versiones anteriores de mysql, actualizar una tabla a la que hace referencia dentro de una subconsulta de la actualización podría fallar. Si es así, solo cree una segunda mesa y llénela (inserciones), luego elimine la anterior y cambie el nombre de la nueva.

+2

+1 Tenía que ser un camino de una sola vez. NB, sin embargo, necesita resembrar la identidad en la primera tabla, otra próxima inserción sabia dejará un espacio. –

+1

gracias. Voy a arreglar esto – Sebas

+0

# 1452 - No se puede agregar o actualizar una fila secundaria: falla una restricción de clave externa ('table2', CONSTRAINT' table2_ibfk_1' FOREIGN KEY ('id_image') REFERENCIAS' table1' ('table1') ON DELETE CASCADE ON UPDATE CASCADE) - ¿Cuál es el problema aquí? –

1

Dolor esto.

Crear una tabla como la primera, sin identidad en Id_Image y una columna int extra llamado rownumber

usar el truco de pseudo row_number poblarlo, algunos como

Insert into NewTable 
Select id_image,foo,bar,@RowNumber := @RowNumber + 1 order by id_image. 

Si usted tiene una clave externa a la segunda mesa soltarlo, luego es una simple actualización con una combinación. Elimine la tabla anterior1, cambie el nombre de la nueva, agregue la identidad y resínela, y vuelva a colocar su clave externa si la tuviera.

¿Te das cuenta de que vas a tener que seguir haciendo esta basura?

Probablemente haya una manera divertida de hacer todo esto de una vez, si tiene actualizaciones en cascada, pero preste mucha atención al plan de ejecución. El truco RowNumber solo funciona si las cosas se hacen en el orden Id_Image. Si Mysql decide que hay una forma más eficiente de hacer la consulta ...

Cuestiones relacionadas