D1 では、データベース内のテーブルをまたいで外部キー制約を定義し、強制できます。
外部キー制約を使うと、テーブル間の関係を強制できます。たとえば、users テーブルの user_id と orders テーブルの user_id を厳密に結びつけると、存在しないユーザーに対する注文は作成できません。
外部キー制約は、ほかのテーブルの行を参照している行の削除も防げます。たとえば、orders テーブルの行が参照している users テーブルの行は削除できません。
デフォルトでは、D1 はすべてのクエリとマイグレーションで外部キー制約が有効であることを強制します。これは、SQLite で各トランザクションに PRAGMA foreign_keys = on を設定したときと同じ動作です。
D1 データベースに対して クエリ、マイグレーション、または データのインポート を実行するとき、テーブル作成やスキーマ変更のあいだ、外部キー検証を無効にしたい場合があります。
D1 の外部キー強制は、SQLite の PRAGMA foreign_keys = on ディレクティブと同等です。D1 はすべてのクエリを暗黙のトランザクション内で実行するため、ユーザーのクエリからこの設定をクエリ中やマイグレーション中に変更することはできません。
代わりに、D1 では PRAGMA defer_foreign_keys = on または off を呼び出せます。これにより、現在のトランザクションが終わるまで、一時的に外部キー制約に違反できます。
PRAGMA defer_foreign_keys = off を呼び出しても、現在のトランザクションの外では外部キーの強制は無効になりません。トランザクションの終了時点で未解決の外部キー違反が残っていると、FOREIGN KEY constraint failed エラーで失敗します。
外部キーの強制を遅延するには、トランザクションの先頭、または制約に違反する変更の前に PRAGMA defer_foreign_keys = on を設定します。
-- Defer foreign key enforcement in this transaction.
PRAGMA defer_foreign_keys = on
-- Run your CREATE TABLE or ALTER TABLE / COLUMN statements
ALTER TABLE users ...
-- This is implicit if not set by the end of the transaction.
PRAGMA defer_foreign_keys = off未解決の外部キー制約を解消した直後に、明示的に PRAGMA defer_foreign_keys = off を設定することもできます。まだ未解決の外部キー制約がある場合は、FOREIGN KEY constraint failed エラーが返り、違反を解消する必要があります。
外部キー関係は、CREATE TABLE でテーブルを作成するとき、または ALTER TABLE で既存テーブルに列を追加するときに定義できます。
例として、2 つのテーブルを持つ e コマースサイトを考えます。
- ユーザーアカウントの共通プロパティと、一意の
user_id識別子を定義するusersテーブル。 - 注文をユーザーテーブルの
user_idに対応づけるordersテーブル。
この対応づけは FOREIGN KEY として定義され、次を保証します。
- 外部キー制約に違反する行を
usersテーブルから削除できません。有効なユーザーに対応づかない注文が残ることはありません。 ordersは常に有効なuser_idに対して定義されるため、無効(または存在しない)ユーザーを参照する注文を作るリスクを下げられます。
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
email_address TEXT,
name TEXT,
metadata TEXT
)
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
status INTEGER,
item_desc TEXT,
shipped_date INTEGER,
user_who_ordered INTEGER,
FOREIGN KEY(user_who_ordered) REFERENCES users(user_id)
)テーブルごとに複数の外部キー関係を定義でき、外部キー定義はデータベーススキーマ内の複数テーブルを参照できます。
外部キー定義の一部として アクション を定義すると、親行(REFERENCES table(column))への変更を制限するか、伝播できます。アクションを定義すると、アプリケーションで外部キー制約の挙動を把握しやすくなり、関連データのクリーンアップや、データの孤立防止に役立ちます。
外部キー関係の ON UPDATE および / または ON DELETE 句には、5 つのアクションを設定できます。要件に応じて、ON UPDATE と ON DELETE に別々のアクションを定義することもできます。
CASCADE- 親キーの更新または削除で、関連するすべての子キー(行)を削除します。RESTRICT- いずれかの子キーが参照している親キーは、更新も削除もできません。デフォルトの外部キー強制と異なり、RESTRICTを適用した関係はトランザクション終了時ではなく、すぐにエラーを返します。SET DEFAULT- 外部キー定義が参照する子列を、スキーマで定義したDEFAULT値に設定します。子列にDEFAULTが設定されていない場合、このアクションは使えません。SET NULL- 外部キー定義が参照する子列を SQL のNULLに設定します。NO ACTION- 何もしません。
次の例では、ON DELETE CASCADE を定義しているため、users テーブルからユーザーを削除すると、scores テーブルの関連行もすべて削除されます。削除したユーザーのスコアを残さない場合は、scores テーブルの関連行をすべて削除します。そうすると、まだ有効だったスコアを ほかの ユーザーが参照できなくなることがあります。
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
email_address TEXT,
)
CREATE TABLE scores (
score_id INTEGER PRIMARY KEY,
game TEXT,
score INTEGER,
player_id INTEGER,
FOREIGN KEY(player_id) REFERENCES users(user_id) ON DELETE CASCADE
)- SQLite の
FOREIGN KEY↗ ドキュメントを読む。 - Worker 内から D1 Workers Binding API を使う 方法を学ぶ。
- D1 での データベースマイグレーションの動き を理解する。