2

私はSQLiteデータベースに取り組んでいます。データベースはすでにいっぱいですが、リファクタリングしたいと思います。これが私がする必要があることのサンプルです:

私は現在1つのテーブルを持っています:

CREATE TABLE Cars (ID INTEGER PRIMARY KEY,
                   Name VARCHAR(32),
                   TopSpeed FLOAT,                   
                   EngineCap FLOAT);

これを2つのテーブルに分割したいと思います。

CREATE TABLE Vehicles (ID INTEGER PRIMARY KEY,
                       Name VARCHAR(32),
                       TopSpeed FLOAT); 

CREATE TABLE Cars (ID INTEGER PRIMARY KEY,
                   VehicleID INTEGER CONSTRAINT FK_Cars REFERENCES [Vehicles](ID),                  
                   EngineCap FLOAT);          

Carsテーブルの内容を使用して一時テーブルを作成することを理解しました。テーブルVehiclesの内容をテーブルに入力できCarsます。

CREATE TEMPORARY TABLE Cars_temp AS SELECT * FROM Cars;

INSERT INTO Vehicles (Name, TopSpeed)
SELECT Name, TopSpeed FROM Cars_temp;

EngineCapしかし、フィールドを新しいCarsテーブルに配置し、テーブルから対応するID値を抽出してテーブルの外部キーフィールドに配置しながら、同じ選択を確認する方法をまだ探しています。VehiclesVehicleIDCars

私は回避策または代替アプローチを受け入れています。

ありがとう。

4

3 に答える 3

2

@mateuszaは例を提供しなかったので、私は1つ作成しました。

次のテーブルがあるとします。

CREATE TABLE [Customer] (
  [name] TEXT,
  [street] TEXT,
  [city] TEXT);

ここで、別のテーブルに移動streetしたいので、2つのテーブルになります。cityAddress

CREATE TABLE [Customer2] (
  [name] TEXT,
  [addr] INTEGER);

CREATE TABLE [Address] (
  [rowid] INTEGER NOT NULL,
  [street] TEXT,
  [city] TEXT,
  PRIMARY KEY ([rowid])
);

(この例では、同じデータベースで変換を行っています。SQLATTACHコマンドを使用して、おそらく2つのDBを使用し、一方を他方に変換します。)

次に、ビュー(新しいテーブルを使用して元のテーブルを模倣します)とトリガーを作成します。

CREATE VIEW Customer1 (name, street, city) AS
    SELECT C.name, A.street, A.city FROM Customer2 AS C
    JOIN Address as A ON (C.addr == A.rowid);

CREATE TEMP TRIGGER TempTrig INSTEAD OF INSERT ON Customer1 FOR EACH ROW BEGIN
    INSERT INTO Address (street, city) SELECT NEW.street, NEW.city;
    INSERT INTO Customer2 (addr, name) SELECT last_insert_rowid(), NEW.name;
END;

これで、テーブルの行をコピーできます。

INSERT INTO Customer1 (name, street, city) SELECT name, street, city FROM Customer;

上記は、一部のデータのみを1つの新しいテーブルに移動する単純化されたケースです。

より複雑な(そしてより一般的な)ケースは、あなたがしたい場合です...

  1. 元のテーブルの列をいくつかの外部テーブルに分割し、
  2. 外部テーブルに一意のエントリがあります(これが通常、テーブルをリファクタリングする理由です)。

これにより、いくつかの追加の課題が追加されます。

  1. 参照するROWIDを使用してそれらのROWIDをテーブルに挿入する前に、複数のテーブルに挿入することになります。これには、それぞれINSERTのlast_insert_rowid()の結果を一時テーブルに保存する必要があります。
  2. 値が外部テーブルにすでに存在する場合は、(実行されていない)挿入操作のROWIDではなく、そのROWIDを格納する必要があります。

これに対する完全な解決策は次のとおりです。曲名、アルバムタイトル、アーティスト名で構成される音楽レコードのデータベースを管理します。

-- Original table
CREATE TABLE [Song] (
  [title] TEXT,
  [album] TEXT,
  [artist] TEXT
);

-- Refactored tables
CREATE TABLE [Song2] (
  [title] TEXT,
  [album_rowid] INTEGER,
  [artist_rowid] INTEGER
);
CREATE TABLE [Album] (
  [rowid] INTEGER PRIMARY KEY AUTOINCREMENT,
  [title] TEXT UNIQUE
);
CREATE TABLE [Artist] (
  [rowid] INTEGER PRIMARY KEY AUTOINCREMENT,
  [name] TEXT UNIQUE
);

-- Fill with sample data

INSERT INTO Song VALUES ("Hunting Girl", "Songs From The Wood", "Jethro Tull");
INSERT INTO Song VALUES ("Acres Wild", "Heavy Horses", "Jethro Tull");
INSERT INTO Song VALUES ("Broadford Bazar", "Heavy Horses", "Jethro Tull");
INSERT INTO Song VALUES ("Statue of Liberty", "White Music", "XTC");
INSERT INTO Song VALUES ("Standing In For Joe", "Wasp Star", "XTC");
INSERT INTO Song VALUES ("Velvet Green", "Songs From The Wood", "Jethro Tull");

-- Conversion starts here

CREATE TEMP TABLE [TempRowIDs] (
  [album_id] INTEGER,
  [artist_id] INTEGER
);

CREATE VIEW Song1 (title, album, artist) AS
  SELECT Song2.title, Album.title, Artist.name
    FROM Song2
    JOIN Album ON (Song2.album_rowid == Album.rowid)
    JOIN Artist ON (Song2.artist_rowid == Artist.rowid);

CREATE TEMP TRIGGER TempTrig INSTEAD OF INSERT ON Song1 FOR EACH ROW BEGIN
  INSERT OR IGNORE INTO Album (title) SELECT NEW.album;
  UPDATE TempRowIDs SET album_id = (SELECT COALESCE (
    (SELECT rowid FROM Album WHERE changes()==0 AND title==NEW.album), last_insert_rowid()
  ) ) WHERE rowid==1;
  INSERT OR IGNORE INTO Artist (name) SELECT NEW.artist;
  UPDATE TempRowIDs SET artist_id = (SELECT COALESCE (
    (SELECT rowid FROM Artist WHERE changes()==0 AND name==NEW.artist), last_insert_rowid()
  ) ) WHERE rowid==1;
  INSERT INTO Song2 (title, album_rowid, artist_rowid) SELECT
    NEW.title, (SELECT album_id FROM TempRowIDs), (SELECT artist_id FROM TempRowIDs);
END;

INSERT INTO TempRowIDs DEFAULT VALUES;

INSERT INTO Song1 (title, album, artist) SELECT title, album, artist FROM Song;

DROP TRIGGER TempTrig;
DROP TABLE TempRowIDs;

-- Conversion ends here

-- Print results
SELECT * FROM Song;
SELECT * FROM Song1;

-- Check if original and copy are identical (https://stackoverflow.com/a/13865679/43615)
SELECT CASE WHEN (SELECT COUNT(*) FROM (SELECT * FROM Song UNION SELECT * FROM Song1)) == (SELECT COUNT() FROM Song) THEN 'Success' ELSE 'Failure' END;

この例には1つの潜在的な問題があることに注意してください。外部テーブルの制約がより複雑な場合は、SELECT rowid FROMそれに応じて既存のエントリの検索を更新する必要があります。理想的には、SQLiteは競合するROWIDを何らかの方法で特定する方法を提供する必要がありますが、残念ながらそうではありません(この関連する質問を参照してください)。

于 2018-11-24T18:32:12.990 に答える
1

トリガーのないシンプルなソリューション:

  • CAR_IDを含むVEHICLES_TEMPテーブルを作成します
  • 不要なVEHICLES列を使用せずに新しいCARSテーブルを作成します
  • VEHICLES_TEMPから取得したVEHICLE_IDでCARSを更新します(CAR_IDで識別されます)
  • CAR_IDなしで最終的なVEHICLESテーブルを作成します
于 2012-06-01T15:49:31.437 に答える
0

New_CarsテーブルとINSTEAD OF INSERTトリガーを作成します。これにより、テーブルVehiclesとの両方にデータが挿入されますCars。に挿入する場合、 last_insert_rowid()関数をCars使用して、テーブルに挿入された行を参照できます。Vehicles

これは一時的な解決策である場合もあれば、データベースに残してさらに変更する場合もあります。

于 2012-06-01T12:39:32.447 に答える