Foreign keys: links between tables
In short: A foreign key (REFERENCES) protects the link between two tables, and ON DELETE says what happens to linked rows on delete: CASCADE, SET NULL or RESTRICT.
Problem
Delete my account
Maria Ivanova writes: “Please delete my account.” You try:
The database refuses. Maria has 12 orders, and each of them points to her. If the customer disappears, the orders will point to no one.
What should happen to her orders? And to her reviews? And to her list of favourite products? The answer is different for each table. And it is written in the schema.
The idea
REFERENCES
You already know the foreign key: a column that points to a row in another table. In CREATE TABLE it is written with REFERENCES table(column). That is how the shop's orders are made:
orders
- PK
idinteger - FK
customer_idinteger *→ customers ordered_attext *statustext *
Here is a new table: each customer's favourite products. The first row goes in, the second points to customer 99, who does not exist:
Now the database guards the links itself. A row that points to a customer or a product that does not exist does not get in.
Try it
The favourite products
The table wishlist already has its columns. Make customer_id point to customers and product_id to products.
Solution
CREATE TABLE wishlist (
customer_id INTEGER NOT NULL REFERENCES customers(id),
product_id INTEGER NOT NULL REFERENCES products(id),
PRIMARY KEY (customer_id, product_id)
);customer_id INTEGER NOT NULL REFERENCES customers(id) and product_id INTEGER NOT NULL REFERENCES products(id). If you write no column after the table name, SQLite assumes you point to its primary key.
The idea
ON DELETE CASCADE
After REFERENCES you can say what should happen when the row you point to is deleted. That is ON DELETE.
CASCADE means “delete me too”. Yavor Penev (customer 20) has two favourite products. Teodora Genova (customer 19) has one. Yavor deletes his account:
Yavor's favourites are gone with him. Teodora's are still there. A list of favourites makes no sense without the customer, so CASCADE is right here.
The idea
SET NULL and RESTRICT
SET NULL means “keep me, but without the link”. Questions about products are useful to other buyers too, even when the author has left:
The question stayed, and customer_id became NULL: a question from a former customer.
RESTRICT means “do not allow the deletion”. Invoices have to be kept for years by law:
Without ON DELETE the behaviour is almost the same: the deletion stops. That is why Maria could not delete her account. The orders are the shop's history and must not disappear.
More precisely, if you are curious
Without ON DELETE the rule is NO ACTION. The only difference from RESTRICT is when it is checked: RESTRICT stops at once, while NO ACTION checks at the end of the statement (or of the transaction, if the check is deferred). There is also ON UPDATE for changing the key, but it is rarely used, because a primary key almost never changes. ON DELETE SET DEFAULT is rare too.
See it
SET NULL against NOT NULL
Someone wrote the questions table like this:
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE SET NULL
Yavor deletes his account again:
SET NULL wants to store NULL, and NOT NULL does not allow it. The deletion stops. SQLite does not warn you about this at CREATE TABLE, only when someone tries to delete. A column with SET NULL must be allowed to be empty.
Try it
The delivery addresses
The table addresses keeps the customers' addresses. When a customer deletes their account, their addresses must disappear with them. Fix customer_id.
Solution
CREATE TABLE addresses (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
city TEXT NOT NULL,
street TEXT NOT NULL
);REFERENCES customers(id) ON DELETE CASCADE. An address without a customer is of no use to anyone, and it is personal data that should not stay behind.
The idea
The check has to be on
In the course the database checks foreign keys. You ask it with PRAGMA, SQLite's command for its settings:
1 means “on”. If you switch it off, REFERENCES no longer guards anything:
An order for customer 99, who does not exist, went in without an error.
More precisely, if you are curious
In real SQLite the check is off by default. Run sqlite3 on your computer and type PRAGMA foreign_keys;: the answer is 0. The reason is old programs written before SQLite checked foreign keys. The setting applies to one connection to the database only. That is why a program runs PRAGMA foreign_keys = ON; every time it opens the database. This page does it for you. PostgreSQL, MySQL (with InnoDB) and SQL Server check foreign keys by default.
Exercise
Questions about products
The table questions has id, product_id, customer_id and body. When a product is removed from the shop, the questions about it disappear. When a customer deletes their account, their questions stay, but without an author. Add the links.
Solution
CREATE TABLE questions (
id INTEGER PRIMARY KEY,
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
body TEXT NOT NULL
);product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE and customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL. customer_id has no NOT NULL, otherwise SET NULL could not work.
Test
Check yourself
Related lessons
Section titled "Related lessons"- Builds on: INSERT: new rows · DELETE: deleting rows · CREATE TABLE: a new table · Constraints: rules for the data
- Continues in: ALTER TABLE: changing a table · Relationship types: 1:1, 1:N, M:N · ER diagram: from a description to a schema · Normalization
- Tasks on this topic: Tables and rules