5

我有两张这样的桌子;

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第二个表中的列,使其值对应于间隙修复之前对应的同一行。

我将如何开始这样做?我知道这可能超出了 MySQL 查询语言的能力,所以 PHP 的答案也是可以接受的。谢谢!:)

4

3 回答 3

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 中,更新您在更新的子查询中引用的表可能会崩溃。如果是这样,只需创建第二个表并填写(插入),然后删除旧表并重命名新表。

于 2012-06-18T02:15:18.157 回答
1

The basic idea here is to find all of the gaps first to determine how much you need to decrement each id. Then, you have to iterate through both tables and apply the decrement. (You'll need to add: host, db, user, pass, and the actual table names)

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

Important: You might want to throw this in a file and run via the CLI: php -f gapfix.php and include a query before $pdo->commit() that returns a list of all the ids so you can verify the operation worked as expected. If it didn't, you can roll it back as if nothing happened. The code now checks for itself if the first table is in the right order. It doesn't however check the second table yet. All checking has been implemented!

于 2012-06-18T02:31:27.470 回答
1

痛这个。

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

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

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

如果您有第二个表的外键删除它,那么这是一个简单的连接更新。删除旧的 table1 ,重命名新的,添加身份和重新种子,如果你有外键,把你的外键放回去。

你真的意识到你将不得不继续做这种废话吗?

如果您有级联更新,可能有一种一次性完成所有这些的有趣方式,但要特别注意执行计划。RowNumber 技巧仅在事情以 Id_Image 顺序完成时才有效。如果 Mysql 决定有一种更有效的查询方式......

于 2012-06-18T02:18:49.830 回答