Topic 23 of 52
FOREIGN KEY
Overview
A FOREIGN KEY creates a link between two tables, enforcing referential integrity — ensuring that a record in one table references a valid record in another. It prevents orphaned records and data inconsistency.
Syntax
sql
-- Foreign key definition
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id), -- shorthand
product_id INT REFERENCES products(id) ON DELETE SET NULL,
...
);
-- Full syntax
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE RESTRICT -- default: prevent deletion of referenced row
ON UPDATE CASCADE -- update FK automatically if PK changes
);
-- ON DELETE actions:
-- RESTRICT: prevent deletion (error if referenced rows exist)
-- CASCADE: delete child rows when parent is deleted
-- SET NULL: set FK to NULL when parent is deleted
-- SET DEFAULT: set FK to default value when parent is deleted
-- NO ACTION: same as RESTRICT (checked at end of transaction)
-- Add FK to existing table
ALTER TABLE orders ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(id);Common Pitfalls
- Always index FK columns — JOINs on unindexed FK columns cause full table scans.
- ON DELETE CASCADE is powerful but dangerous — deleting one parent row can delete thousands of child rows across multiple tables.
- Interview tip: Foreign keys enforce data integrity at the database level, not just the application level — essential for multi-application databases.
Real-World Example
Foreign key design for an e-commerce schema:
example
sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
-- RESTRICT: can't delete a user who has orders (protect history)
status VARCHAR(20) DEFAULT 'pending'
);
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
-- CASCADE: deleting an order deletes its items too (makes sense!)
product_id INT NOT NULL REFERENCES products(id) ON DELETE RESTRICT
-- RESTRICT: can't delete a product that has been ordered
);
-- Index FK columns! (critical for join performance)
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);