Define foreign keys
D1 supports defining and enforcing foreign key constraints across tables in a database.
Foreign key constraints allow you to enforce relationships across tables. For example, you can use foreign keys to create a strict binding between a user_id
in a users
table and the user_id
in an orders
table, so that no order can be created against a user that does not exist.
Foreign key constraints can also prevent you from deleting rows that reference rows in other tables. For example, deleting rows from the users
table when rows in the orders
table refer to them.
By default, D1 enforces that foreign key constraints are valid within all queries and migrations. This is identical to the behaviour you would observe when setting PRAGMA foreign_keys = on
in SQLite for every transaction.
When running a query, migration or importing data against a D1 database, there may be situations in which you need to disable foreign key validation during table creation or changes to your schema.
D1's foreign key enforcement is equivalent to SQLite's PRAGMA foreign_keys = on
directive. Because D1 runs every query inside an implicit transaction, user queries cannot change this during a query or migration.
Instead, D1 allows you to call PRAGMA defer_foreign_keys = on
or off
, which allows you to violate foreign key constraints temporarily (until the end of the current transaction).
Calling PRAGMA defer_foreign_keys = off
does not disable foreign key enforcement outside of the current transaction. If you have not resolved outstanding foreign key violations at the end of your transaction, it will fail with a FOREIGN KEY constraint failed
error.
To defer foreign key enforcement, set PRAGMA defer_foreign_keys = on
at the start of your transaction, or ahead of changes that would violate constraints:
You can also explicitly set PRAGMA defer_foreign_keys = on
immediately after you have resolved outstanding foreign key constraints. If there are still outstanding foreign key constraints, you will receive a FOREIGN KEY constraint failed
error and will need to resolve the violation.
A foreign key relationship can be defined when creating a table via CREATE TABLE
or when adding a column to an existing table via an ALTER TABLE
statement.
To illustrate this with an example based on an e-commerce website with two tables:
- A
users
table that defines common properties about a user account, including a uniqueuser_id
identifier. - An
orders
table that maps an order back to auser_id
in the user table.
This mapping is defined as FOREIGN KEY
, which ensures that:
- You cannot delete a row from the
users
table that would violate the foreign key constraint. This means that you cannot end up with orders that do not have a valid user to map back to. orders
are always defined against a validuser_id
, mitigating the risk of creating orders that refer to invalid (or non-existent) users.
You can define multiple foreign key relationships per-table, and foreign key definitions can reference multiple tables within your overall database schema.
You can define actions as part of your foreign key definitions to either limit or propagate changes to a parent row (REFERENCES table(column)
). Defining actions makes using foreign key constraints in your application easier to reason about, and help either clean up related data or prevent data from being islanded.
There are five actions you can set when defining the ON UPDATE
and/or ON DELETE
clauses as part of a foreign key relationship. You can also define different actions for ON UPDATE
and ON DELETE
depending on your requirements.
CASCADE
- Updating or deleting a parent key deletes all child keys (rows) associated to it.RESTRICT
- A parent key cannot be updated or deleted when any child key refers to it. Unlike the default foreign key enforcement, relationships withRESTRICT
applied return errors immediately, and not at the end of the transaction.SET DEFAULT
- Set the child column(s) referred to by the foreign key definition to theDEFAULT
value defined in the schema. If noDEFAULT
is set on the child columns, you cannot use this action.SET NULL
- Set the child column(s) referred to by the foreign key definition to SQLNULL
.NO ACTION
- Take no action.
In the following example, deleting a user from the users
table will delete all related rows in the scores
table as you have defined ON DELETE CASCADE
. Delete all related rows in the scores
table if you do not want to retain the scores for any users you have deleted entirely. This might mean that other users can no longer look up or refer to scores that were still valid.
- Read the SQLite
FOREIGN KEY
↗ documentation. - Learn how to use the D1 Workers Binding API from within a Worker.
- Understand how database migrations work with D1.