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


128

次の表があります。

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

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

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


SQLite ALTERコマンドは、「テーブルの名前変更」と「列の追加」のみをサポートします。ただし、単純な一連の操作を使用して、テーブルの形式に他の任意の変更を加えることができます。私の答えを
状況

回答:


198

できません。

テーブルに外部キーを追加するSQL-92構文は次のようになります。

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

SQLiteはADD CONSTRAINTALTER TABLEコマンドのバリアントをサポートしいませんsqlite.org:SQLiteが実装しないSQL機能)。

したがって、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


28
古いテーブルの名前を変更し、新しいテーブルを作成してデータをコピーして戻す方が簡単だと思います。その後、古いテーブルを削除できます。
tuinstoel 2009

はい、それは簡単です。私はただsqlite FAQを引用していました:sqlite.org/faq.html#q11。実際には、RENAME TOいくつかの一つでありALTER TABLE、現在のsqlite 3でサポートされているバリアント
ダニエルVassallo氏

3
すべきではない:FOREIGN KEY(parent_id)REFERENCES parent(id)真、ジョナサンは「親テーブル」の名前を与えなかった。実際、テーブルはpersonという名前にする必要がありますが、...
igorludi

3
これは私にとって大きな問題のようです。通常、データベースをダンプするときは、最初にCREATE TABLEコマンドをエクスポートします。次に、INSERT INTOコマンド、最後にADD CONSTRAINTコマンドを実行します。データに循環(外部キー値)依存関係がある場合、外部キーが適用されている間はデータを挿入できません。しかし、後で外部キー制約を追加できない場合は、行き詰まります。もちろん、遅延制約がありますが、これは非常に不格好です。
nagylzs 2014年

9
他のテーブルがこのテーブルへの参照を持っている場合、最初のコメントで述べられているように古いテーブルの名前を変更しないでください!この場合、このすべてのテーブルも再作成する必要があります。
Rocknow、2014

57

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

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

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

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

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

2
このシーケンスに慣れるのは良いですが、これは実際の質問には答えません。既存の制約に制約を追加したいのですが。
ウルフ

9

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

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

  1. 外部キー制約が有効になっている場合は、PRAGMA foreign_keys = OFFを使用してそれらを無効にします。
  2. トランザクションを開始します。
  3. テーブルXに関連付けられているすべてのインデックスとトリガーの形式を覚えておいてください。この情報は、以下の手順8で必要になります。これを行う1つの方法は、次のようなクエリを実行することです:SELECTタイプ、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制約の追加、または列のデータ型の変更などに適しています。


4

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

あなたの具体的な例について:

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

Firefoxアドオンsqlite-managerを使用している場合は、次の操作を実行できます。

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

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

FOREIGN KEY (parent_id) REFERENCES parent(id)

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

リファレンス: Sqlite Manager


2

あなたはこれを試すことができます:

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

-1

基本的にはできませんが、状況を回避できます。

外部キー制約を既存のテーブルに追加する正しい方法は、次のコマンドです。

db.execSQL("alter table child add column newCol integer REFERENCES parent(parent_Id)");

そして、コピーPARENT_IDのにデータをNEWCOLしてから削除PARENT_IDの列が。したがって、一時テーブルは必要ありません。


質問をよく読んでいないようです。問題は、制約のある列を追加するのではなく、外部制約のみを追加することでした。
ウルフ

いいえ。尋ねられた質問には答えません。
MK

-4

最初の子テーブルの列を追加しCidint、その後alter table、以下のコードで。このようにして、外部キーCidを親テーブルの主キーとして追加し、それを子テーブルの外部キーとして使用することができます。

ALTER TABLE [child] 
  ADD CONSTRAINT [CId] 
  FOREIGN KEY ([CId]) 
  REFERENCES [Parent]([CId]) 
  ON DELETE CASCADE ON UPDATE NO ACTION;
GO

1
これはSQLiteでは無効です。また、これはMS SQL構文です。
StilesCrisis 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.