MySQL修复了两个表中的自动增量空白

时间:2012-06-18 01:57:31

标签: php mysql database auto-increment

我有两张桌子;

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

第一个表格中的自动增量id_image存在差距。在第二个表格中,id_image引用第一个表格中的id_image,其中每个ID都有两个。

注意:此表是理论上的。我不知道哪里差距正是如此,或者是否存在多个间隙。我所知道的是,第一个值是1,最后一个值高于总行数。

现在,我想解决这个问题。

在你说差距无关紧要之前,如果他们这样做,这是糟糕的数据库设计,让我告诉你; 我同意你的观点。

然而,我正在处理的是一个(绝望的后端倒退)第三方开源系统,我需要将大量现有数据导入到多个表中,这些数据没有交叉引用的ID。我可以确保相同的数据在整个系统的每个表中获得匹配ID的唯一方法是按顺序输入,这意味着我不能有间隙。

所以我现在需要做的是;

  1. 修复第一个表中id_image列的间隙,以便最后一个值与行数匹配。
  2. 编辑第二个表中的id_image列,使其值对应于同一行,与缺口修复前对应。
  3. 我将如何开始这样做?我知道这可能超出了MySQL查询语言的功能,因此PHP答案也是可以接受的。谢谢! :)

3 个答案:

答案 0 :(得分: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会相应地自动更新第二张桌子的ID。

我根本不确定,但在某些旧版本的mysql中,更新一个在更新子查询中引用的表可能会崩溃。如果是这样,只需创建第二个表并填充(插入),然后删除旧表并重命名新表。

答案 1 :(得分:1)

痛苦的。

创建一个像第一个表,Id_Image上没有标识的表和一个名为rownumber的额外int列

使用伪row_number技巧填充它,有些像

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

如果您有第二个表的外键删除它,那么它是一个带连接的简单更新。删除旧的table1,重命名新的table1,添加标识并重新设置,如果有的话,将外键放回去。

你意识到你将不得不继续做这个废话吗?

如果你有级联更新,可能有一种有趣的方法可以一次完成所有这些,但要注意执行计划。只有在按Id_Image顺序完成操作时,RowNumber技巧才有效。如果Mysql决定采用更有效的方式进行查询......

答案 2 :(得分:1)

这里的基本思想是首先找出所有的差距,以确定减少每个id需要多少。然后,您必须遍历两个表并应用减量。 (您需要添加:host,db,user,pass和实际的表名)

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()之前包含一个返回列表的查询所有ID,以便您可以验证操作是否按预期工作。如果没有,您可以将其回滚,就像没有发生任何事情一样。 如果第一个表的顺序正确,代码现在会检查自己。但是它还没有检查第二个表格。 所有检查都已实施!