2012-06-18 5 views
5

У меня есть две таблицы:Исправление ошибок автоинкремента MySQL в двух таблицах

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 

Существует разрыв в autoincremented id_image в первой таблице. Во второй таблице id_image относится к id_image в первой таблице, и там есть два из каждого идентификатора.

Примечание: Эта таблица теоретическая. Я понятия не имею , где пробел в точности, или есть или нет множественные зазоры. Все, что я знаю, это то, что первое значение - 1, а последнее значение больше, чем общее количество строк.

Теперь я хотел бы исправить этот пробел.

Прежде чем вы скажете, что пробелы не имеют значения, и если они это сделают, это плохой дизайн базы данных, позвольте мне рассказать вам; Я согласен с тобой.

Тем не менее, я имею дело с (без безнадежной обратной стороной назад) сторонней системой с открытыми исходными кодами, в которую мне нужно импортировать огромное количество существующих данных, которые не имеют перекрестно ссылаемых идентификаторов в несколько столы. Единственный способ, которым я могу убедиться, что одни и те же данные получают соответствующий идентификатор в каждой таблице во всей системе, - это ввести его последовательно, а это значит, что я не могу иметь пробелов.

Так что теперь я должен это делать;

  1. Исправить пробел в столбце id_image в первой таблице, чтобы последнее значение соответствовало количеству строк.
  2. Редактировать столбец id_image во второй таблице так, чтобы его значение соответствовало той же строке, что и перед исправлением пробела.

Как бы я начал это делать? Я понимаю, что это может быть вне возможностей языка запросов MySQL, поэтому ответы на PHP также приемлемы. Благодаря! :)

+0

Это двигатель innodb? – Sebas

+0

Да, это так. Однако разрыв был вызван необходимостью повторного ввода части данных, а не ошибкой INSERT IGNORE. :) –

+0

Можете ли вы заблокировать таблицу, а затем перебрать ее с помощью «SELECT image_id FORM table ORDER BY image_id ASC' и найти пробелы? –

ответ

1

Основная идея заключается в том, чтобы сначала найти все пробелы, чтобы определить, сколько вам нужно уменьшить каждый идентификатор. Затем вам нужно пройти через обе таблицы и применить декремент. (Вы должны добавить: хозяин, дб, пользователь, передавать, и фактические имена таблиц)

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); 
} 

Важно: Вы можете бросить это в файл и запустить через CLI: php -f gapfix.php и включить запрос до $pdo->commit(), который возвращает список всех идентификаторов, чтобы вы могли проверить, что работа выполнена должным образом. Если это не так, вы можете откинуть назад, как будто ничего не произошло.Код теперь проверяет себя, если первая таблица находится в правильном порядке. Тем не менее, он не проверяет вторую таблицу.Вся проверка была выполнена!

+0

Вы работаете над этим, проверяя вторую таблицу? :) –

+0

@EmphramStavanger Добавляем его, как мы говорим ... –

+0

@EmphramStavanger Выполнено! –

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; 

FK автоматически обновит идентификаторы вашей второй таблицы соответственно.

Я не совсем уверен, но в некоторых более старых версиях mysql обновляется таблица, в которую вы ссылаетесь в подзапросе обновления, может произойти сбой. Если это так, просто создайте вторую таблицу и заполните ее (вставки), затем удалите старый и переименуйте новую.

+2

+1 Должно быть, это один путь. NB, однако, нужно повторить идентификацию на первой таблице, другая мудрая следующая вставка оставит пробел. –

+1

спасибо. Я исправлю это – Sebas

+0

# 1452 - Не могу добавить или обновить дочернюю строку: ограничение внешнего ключа не выполняется ('table2', CONSTRAINT' table2_ibfk_1' FOREIGN KEY ('id_image') ССЫЛКИ' table1' ('table1') ON DELETE CASCADE ПО ОБНОВЛЕНИЮ КАСКАДА) - В чем проблема? –

1

Болезненное это.

Создать таблицу как и первый, не тождественный на Id_Image и дополнительный столбец INT не называется RowNumber

Используйте псевдо row_number трюк, чтобы заполнить его, некоторые, как

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

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

Вы понимаете, что вам нужно продолжать делать это дерьмо?

Возможно, вам будет интересно сделать все это за один раз, если у вас есть каскадные обновления, но обратите пристальное внимание на план выполнения. Трюк RowNumber работает только в том случае, если все сделано в порядке Id_Image. Если Mysql решает, что существует более эффективный способ выполнения запроса ...

Смежные вопросы