web-dev-qa-db-ja.com

既存のSQLiteテーブルに外部キーを追加するにはどうすればよいですか?

次の表があります。

CREATE TABLE child( 
  id INTEGER PRIMARY KEY, 
  parent_id INTEGER, 
  description TEXT);

parent_idに外部キー制約を追加するにはどうすればよいですか?外部キーが有効になっていると仮定します。

ほとんどの例は、テーブルを作成していることを前提としています。既存の制約に制約を追加したいと思います。

116
Dane O'Connor

できません。

テーブルに外部キーを追加するSQL-92構文は次のとおりです。

ALTER TABLE child ADD CONSTRAINT fk_child_parent
                  FOREIGN KEY (parent_id) 
                  REFERENCES parent(id);

SQLiteはADD CONSTRAINTコマンドのALTER TABLEバリアントをサポートしていません(- sqlite.org:SQL Features That SQLiteは実装しません )。

したがって、sqlite 3.6.1で外部キーを追加する唯一の方法は、次のようにCREATE TABLE中にあります。

CREATE TABLE child ( 
    id           INTEGER PRIMARY KEY, 
    parent_id    INTEGER, 
    description  TEXT,
    FOREIGN KEY (parent_id) REFERENCES parent(id)
);

残念ながら、既存のデータを一時テーブルに保存し、古いテーブルを削除し、FK制約を使用して新しいテーブルを作成してから、一時テーブルからデータをコピーする必要があります。 ( sqlite.org-FAQ:Q11

184
Daniel Vassallo

テーブルを変更し、制約を使用する列を追加すると、制約を追加できます。

まず、parent_idなしでテーブルを作成します。

CREATE TABLE child( 
  id INTEGER PRIMARY KEY,  
  description TEXT);

次に、テーブルを変更します。

ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);
50
Jorge Novaes

確認してください https://www.sqlite.org/lang_altertable.html#otheralter

SQLiteで直接サポートされている唯一のスキーマ変更コマンドは、上記の「テーブル名の変更」および「列の追加」コマンドです。ただし、アプリケーションは、単純な一連の操作を使用して、テーブルの形式に他の任意の変更を加えることができます。一部のテーブルXのスキーマ設計に任意の変更を加える手順は次のとおりです。

  1. 外部キー制約が有効になっている場合、PRAGMA foreign_keys = OFFを使用してそれらを無効にします。
  2. トランザクションを開始します。
  3. テーブルXに関連付けられているすべてのインデックスとトリガーの形式を覚えておいてください。この情報は、以下の手順8で必要になります。これを行う1つの方法は、次のようなクエリを実行することです:SELECT type、sql FROM sqlite_master WHERE tbl_name = 'X'。
  4. CREATE TABLEを使用して、テーブルXの修正された形式の新しいテーブル "new_X"を作成します。もちろん、名前 "new_X"が既存のテーブル名と衝突しないようにしてください。
  5. INSERT INTO new_X SELECT ... FROM Xなどのステートメントを使用して、Xからnew_Xにコンテンツを転送します。
  6. 古いテーブルXをドロップします:DROP TABLEX。
  7. ALTER TABLE new_X RENAME TO Xを使用して、new_Xの名前をXに変更します。
  8. CREATE INDEXとCREATE TRIGGERを使用して、テーブルXに関連付けられたインデックスとトリガーを再構築します。おそらく、上記の手順3で保存したトリガーとインデックスの古い形式をガイドとして使用し、変更に応じて変更を加えます。
  9. スキーマの変更の影響を受ける方法でビューがテーブルXを参照する場合、DROP VIEWを使用してそれらのビューを削除し、CREATE VIEWを使用してスキーマの変更に対応するために必要な変更を加えて再作成します。
  10. 外部キー制約が最初に有効化されていた場合、PRAGMA foreign_key_checkを実行して、スキーマの変更が外部キー制約を壊していないことを確認します。
  11. 手順2で開始したトランザクションをコミットします。
  12. 外部キー制約が最初に有効になっていた場合は、ここで再度有効にします。

上記の手順は完全に一般的なものであり、スキーマの変更によってテーブルに格納されている情報が変更された場合でも機能します。したがって、上記の完全な手順は、列の削除、列の順序の変更、UNIQUE制約またはPRIMARY KEYの追加または削除、CHECKまたはFOREIGN KEYまたはNOT NULL制約の追加、列のデータ型の変更などに適しています。

8
situee

はい、できます。新しい列を追加しなくてもかまいません。データベースの破損を防ぐために、正しく行うように注意する必要があるため、これを試みる前にデータベースを完全にバックアップする必要があります。

あなたの特定の例:

CREATE TABLE child(
  id INTEGER PRIMARY KEY,
  parent_id INTEGER,
  description TEXT
);

--- create the table we want to reference
create table parent(id integer not null primary key);

--- now we add the foreign key
pragma writable_schema=1;
update SQLITE_MASTER set sql = replace(sql, 'description TEXT)',
    'description TEXT, foreign key (parent_id) references parent(id))'
) where name = 'child' and type = 'table';

--- test the foreign key
pragma foreign_keys=on;
insert into parent values(1);
insert into child values(1, 1, 'hi'); --- works
insert into child values(2, 2, 'bye'); --- fails, foreign key violation

またはより一般的に:

pragma writable_schema=1;

// replace the entire table's SQL definition, where new_sql_definition contains the foreign key clause you want to add
UPDATE SQLITE_MASTER SET SQL = new_sql_definition where name = 'child' and type = 'table';

// alternatively, you might find it easier to use replace, if you can match the exact end of the sql definition
// for example, if the last column was my_last_column integer not null:
UPDATE SQLITE_MASTER SET SQL = replace(sql, 'my_last_column integer not null', 'my_last_column integer not null, foreign key (col1, col2) references other_table(col1, col2)') where name = 'child' and type = 'table';

pragma writable_schema=0;

いずれにしても、変更を行う前に、まずSQL定義が何であるかを最初に確認する必要があります。

select sql from SQLITE_MASTER where name = 'child' and type = 'table';

Replace()アプローチを使用する場合、実行する前にreplace()コマンドを最初にテストすることは、実行する前に役立つことがあります。

select replace(sql, ...) from SQLITE_MASTER where name = 'child' and type = 'table';
3
mwag

Firefoxアドオンsqlite-managerを使用している場合、次のことができます:

テーブルを再度ドロップして作成する代わりに、このように変更することができます。

[列]テキストボックスで、リストされている最後の列名を右クリックしてコンテキストメニューを表示し、[列の編集]を選択します。 TABLE定義の最後の列がPRIMARY KEYである場合、最初に新しい列を追加してから、FOREIGN KEY定義を追加するために新しい列の列タイプを編集する必要があることに注意してください。 [列の種類]ボックス内に、コンマを追加し、

FOREIGN KEY (parent_id) REFERENCES parent(id)

データ型の後の定義。 [変更]ボタンをクリックし、[危険な操作]ダイアログボックスの[はい]ボタンをクリックします。

参照: Sqlite Manager

3
Baso

これを試すことができます:

ALTER TABLE [Child] ADD COLUMN column_name INTEGER REFERENCES parent_table_name(column_id);
1
Jamshy EK