13

テーブル A とテーブル B があるとします。テーブル B はテーブル A を参照しています。テーブル A とテーブル B の一連の行をディープ コピーしたいと考えています。新しいテーブル B のすべての行が新しいテーブル A の行を参照するようにしたいと考えています。

行を他のテーブルにコピーしていないことに注意してください。テーブル A の行はテーブル A にコピーされ、テーブル B の行はテーブル B にコピーされます。

外部キー参照がコピーの一部として再調整されるようにするにはどうすればよいですか?

明確にするために、これを行う一般的な方法を見つけようとしています。私が示している例には 2 つのテーブルが含まれていますが、実際の依存関係グラフはもっと複雑になる場合があります。作業を行うために SQL を動的に生成する一般的な方法でも問題ありません。

アップデート:

なぜこれが必要なのかと聞かれるので、背景を説明します。多すぎるかもしれませんが、次のようになります。

クライアント サーバー モデルに移行した古いデスクトップ アプリケーションを使用しています。ただし、アプリケーションは依然として基本的な社内バイナリ ファイル形式を使用して、テーブルのデータを格納しています。データ ファイルはヘッダーの後に一連の行が続くものであり、各行はバイナリ シリアル化されたフィールド値であり、その順序はスキーマ テキスト ファイルによって決定されます。それについての唯一の良いことは、それが非常に速いということです。それは他のすべての点でひどいです。アプリケーションを SQL Server に移行し、パフォーマンスをあまり低下させないようにしています。

これはスケジューリング アプリケーションの一種です。データは誰にとっても重要ではなく、監査追跡なども必要ありません。これは膨大な量のデータではなく、データベースが大きくなりすぎた場合に、必ずしも非常に古いデータを保持する必要はありません。

彼らが慣れ親しんでいる機能の 1 つは、スケジュール全体を複製して、いじくり回すことができる「what-if」シナリオを作成する機能です。どのユーザーも、これを何度でも、何度でも実行できます。古いデータベースでは、各スケジュールのデータ ファイルは、名前で識別される独自のデータ フォルダーに保存されます。そのため、スケジュールのコピーは、データ フォルダーをコピーして名前を変更するのと同じくらい簡単でした。

SQL Server で同じことを効果的に実行できなければ、移行は機能しません。冗長性を避けるために、実際に変更されるデータのみをコピーできると考えているかもしれません。しかし、それは正直なところ、実行するには複雑すぎるように思えます。

ミックスに別のレンチを投入するために、スケジュール データ フォルダーの階層が存在する可能性があります。したがって、データ フォルダにはデータ フォルダが含まれる場合があり、データ フォルダにはデータ フォルダが含まれる場合があります。また、コピーはどのレベルでも発生する可能性があります。

SQL Server では、これを模倣するためにネストされたセット階層を実装しています。次のような DATA_SET テーブルがあります。

CREATE TABLE dbo.DATA_SET
(
    DATA_SET_ID UNIQUEIDENTIFIER PRIMARY KEY,
    NAME NVARCHAR(128) NOT NULL,
    LFT INT NOT NULL,
    RGT INT NOT NULL
)

つまり、データセットのツリー構造があります。各データ セットはスケジュールを表し、子データ セットを含む場合があります。すべてのテーブルのすべての行には、属しているデータ セットを示す DATA_SET_ID FK 参照があります。データ セットをコピーするときは常に、そのデータ セットのテーブル内のすべての行と他のすべてのデータ セットを同じテーブルにコピーしますが、新しいデータ セットを参照します。

だから、ここに簡単で具体的な例があります:

CREATE TABLE FOO
(
    FOO_ID BIGINT PRIMARY KEY,
    DATA_SET_ID BIGINT FOREIGN KEY REFERENCES DATA_SET(DATA_SET_ID) NOT NULL
)


CREATE TABLE BAR
(
    BAR_ID BIGINT PRIMARY KEY,
    DATA_SET_ID BIGINT FOREIGN KEY REFERENCES DATA_SET(DATA_SET_ID) NOT NULL,
    FOO_ID UNIQUEIDENTIFIER PRIMARY KEY
)

INSERT INTO FOO
SELECT 1, 1 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 1 UNION ALL

INSERT INTO BAR
SELECT 1, 1, 1
SELECT 2, 1, 2
SELECT 3, 1, 3

では、データ セット 1 を ID 2 の新しいデータ セットにコピーするとします。コピー後、テーブルは次のようになります。

FOO
FOO_ID, DATA_SET_ID
1    1
2    1
3    1
4    2
5    2
6    2

BAR
BAR_ID, DATA_SET_ID, FOO_ID
1    1    1
2    1    2
3    1    3
4    2    4
5    2    5
6    2    6

ご覧のとおり、新しい BAR 行は新しい FOO 行を参照しています。私が求めているのは、DATA_SET_ID の再配線ではありません。一般的に外部キーの再配線について質問しています。

というわけで、確かに情報が多すぎましたが、これで終わりです。

このようにデータを一括コピーするという考え方には、パフォーマンスに関する懸念がたくさんあると思います。テーブルは巨大になることはありません。どのテーブルにも 1000 を超えるレコードがあるとは考えていません。ほとんどのテーブルはそれよりはるかに小さくなります。古いデータ セットは、何の影響もなく完全に削除できます。

ありがとう、テダーツ

4

2 に答える 2

2

これは、おそらくあなたが始めることができる 3 つのテーブルの例です。

DB スキーマ

CREATE TABLE users
    (user_id int auto_increment PRIMARY KEY, 
     user_name varchar(32));
CREATE TABLE agenda
    (agenda_id int auto_increment PRIMARY KEY, 
     `user_id` int, `agenda_name` varchar(7));
CREATE TABLE events
    (event_id int auto_increment PRIMARY KEY, 
     `agenda_id` int, 
     `event_name` varchar(8));

ユーザーのアジェンダとイベントの記録を複製する SP

DELIMITER $$
CREATE PROCEDURE clone_user(IN uid INT)
BEGIN
    DECLARE last_user_id INT DEFAULT 0;

    INSERT INTO users (user_name)
    SELECT user_name
      FROM users
     WHERE user_id = uid;

    SET last_user_id = LAST_INSERT_ID();

    INSERT INTO agenda (user_id, agenda_name)
    SELECT last_user_id, agenda_name
      FROM agenda
     WHERE user_id = uid;

    INSERT INTO events (agenda_id, event_name)
    SELECT a3.agenda_id_new, e.event_name
      FROM events e JOIN
    (SELECT a1.agenda_id agenda_id_old, 
           a2.agenda_id agenda_id_new
      FROM
    (SELECT agenda_id, @n := @n + 1 n 
       FROM agenda, (SELECT @n := 0) n 
      WHERE user_id = uid 
      ORDER BY agenda_id) a1 JOIN
    (SELECT agenda_id, @m := @m + 1 m 
       FROM agenda, (SELECT @m := 0) m 
      WHERE user_id = last_user_id 
      ORDER BY agenda_id) a2 ON a1.n = a2.m) a3 
         ON e.agenda_id = a3.agenda_id_old;
END$$
DELIMITER ;

ユーザーのクローンを作成するには

CALL clone_user(3);

これがSQLFiddle のデモです。

于 2013-06-05T05:14:36.607 に答える