Constraints and data integrity
Foreign keys and what happens on delete
The idea in 30 seconds
A foreign key stops you creating a reference to nothing, and also stops you deleting a row that others still point to. What happens on delete is your choice: refuse (the default), delete the dependent rows too, or clear the reference. And because PostgreSQL does not index the referencing column for you, decide whether it needs one.
Helps to know first: SQL types and the type system
On this page
References in both directions
You have seen a foreign key refuse an order for a customer who does not exist. It also guards the other direction: you cannot delete a customer who still has orders.
DELETE FROM customers WHERE id = 1;
ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"
DETAIL: Key (id)=(1) is still referenced from table "orders".
Asha has three orders, so deleting her would leave three orders pointing at nobody. Elena has none, so deleting her works:
DELETE FROM customers WHERE id = 5 RETURNING name; -- Elena Petrova, DELETE 1
Choosing what a delete does
When you declare a foreign key, you choose what happens to the referencing rows when the referenced row is deleted:
ON DELETE | What happens to the orders when a customer is deleted | Use when |
|---|---|---|
NO ACTION (default) | The delete fails while orders still exist | The dependent rows matter on their own; deletion should be a deliberate decision |
RESTRICT | Same, and the check cannot be postponed | As above, strictly |
CASCADE | The orders are deleted too | The child rows are part of the parent and meaningless without it |
SET NULL | The orders keep existing, with customer_id set to NULL | The child can stand alone, and the column allows NULL |
CASCADE is convenient and dangerous. We tried it on a scratch copy: a customer with two orders, ON DELETE CASCADE, one DELETE of the customer. Afterwards, zero orders were left, and nothing warned us.
Index the referencing column
Declaring orders.customer_id REFERENCES customers (id) does not create an index on orders.customer_id. The referenced side always has one, because it must be a primary key or unique. The referencing side is left to you.
That matters in two places:
- Deleting or changing a customer makes PostgreSQL look for orders that reference them. Without an index on
orders.customer_id, that is a scan of the wholeorderstable, for every customer you delete. - Joining orders to customers by customer, as in “this customer’s orders”, is the lookup you met in B-tree indexes.
On a large table you will usually want CREATE INDEX ON orders (customer_id);. The practice database leaves it out because the table is tiny, and that is a decision you should make deliberately, not by accident.
Check yourself
What is the default
ON DELETEbehavior, and what does it do when references remain?Show answer
NO ACTION. The delete is refused with a foreign key violation error while referencing rows still exist.When is
ON DELETE CASCADEappropriate, and when is it dangerous?Show answer
It is appropriate when the child rows are part of the parent and meaningless without it, such as order items of an order. It is dangerous for records that matter on their own, such as orders of a customer, because one delete silently removes them all.
Why consider an index on a foreign key column?
Show answer
PostgreSQL does not create one automatically. Without it, deleting or updating a referenced row has to scan the whole referencing table to look for references, and lookups such as “this customer’s orders” cannot use an index.
Finished reading?
Saved in this browser only.
Sources
Where each claim on this page comes from.
- 1.PostgreSQL documentation: 5.5 ConstraintsPrimary· §5.5.5 Foreign Keys
The default ON DELETE action is NO ACTION, which usually results in an error if references remain; RESTRICT, CASCADE, SET NULL and SET DEFAULT are the alternatives; a foreign key does not automatically create an index on the referencing columns, and indexing them is often a good idea.
Go further
What to read next, and why it is worth the time.
- 1.PostgreSQL documentation: 5.5 ConstraintsPrimary· §5.5.5 Foreign Keys
Includes an orders and products example that shows choosing a different delete rule for each reference.
Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.