5

私はそのような2つのテーブルを持っています。

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最初のテーブルの自動インクリメントにはギャップがあります。2番目の表では、は最初の表のid_imageを参照しておりid_image、そこには各IDが2つあります。

注意:この表は理論上のものです。ギャップが正確にどこにあるのか、あるいは複数のギャップがあるかどうかさえわかりません。私が知っているのは、最初の値が1であり、最後の値が合計行数よりも多いということだけです。

さて、このギャップを修正したいと思います。

ギャップは問題ではないと言う前に、もしそうなら、それは悪いデータベース設計です、私に言わせてください。仰るとおりです。

ただし、私が扱っているのは、相互参照可能なIDを持たない大量の既存のデータを複数のテーブルにインポートする必要があるサードパーティのオープンソースシステムです。システム全体のすべてのテーブルで同じデータが一致するIDを取得することを確認できる唯一の方法は、それを順番に入力することです。つまり、ギャップを設けることはできません。

だから私が今しなければならないことは;

  1. id_image最後の値が行数と一致するように、最初のテーブルの列のギャップを修正します。
  2. 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は、それに応じて2番目のテーブルのIDを自動的に更新します。

まったくわかりませんが、一部の古いバージョンのmysqlでは、更新のサブクエリ内で参照しているテーブルを更新すると、クラッシュする可能性があります。その場合は、2番目のテーブルを作成して入力(挿入)してから、古いテーブルを削除して新しいテーブルの名前を変更します。

于 2012-06-18T02:15:18.157 に答える
1

これは痛い。

最初のようなテーブルを作成します。Id_ImageにはIDがなく、rownumberという名前の追加のint列があります。

疑似row_numberトリックを使用して、データを入力します。

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

2番目のテーブルへの外部キーがある場合は、それをドロップします。これは、結合を使用した単純な更新です。古いテーブル1を削除し、新しいテーブルの名前を変更し、IDと再シードを追加して、外部キーがある場合は元に戻します。

あなたはあなたがこのがらくたをし続けなければならないだろうと気づいていますか?

カスケード更新をオンにしている場合は、これらすべてを一度に実行する楽しい方法がおそらくありますが、実行計画には細心の注意を払ってください。RowNumberトリックは、Id_Imageの順序で実行された場合にのみ機能します。Mysqlがクエリを実行するより効率的な方法があると判断した場合...

于 2012-06-18T02:18:49.830 に答える
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のリストが返されるため、操作が期待どおりに機能したことを確認できます。そうでない場合は、何も起こらなかったかのようにロールバックできます。 コードは、最初のテーブルが正しい順序であるかどうかをチェックするようになりました。ただし、2番目のテーブルはまだチェックされていません。 すべてのチェックが実装されました!

于 2012-06-18T02:31:27.470 に答える