Database Design
One-to-Many Relationships
Master the most common database relationship by designing foreign keys, understanding cardinality constraints, and implementing referential integrity in BookVault's order system.
What Makes One-to-Many Special
One-to-many relationships dominate real-world databases. Think about it: one customer places many orders. One author writes many books. One category contains many products.
Amazon runs on thousands of one-to-many relationships. Each customer has multiple orders, wishlists, and reviews. Each product belongs to one seller but gets many reviews. The relationship appears everywhere because it mirrors how we organize information naturally.
But here's what trips up most beginners: the foreign key always goes on the "many" side. Always. No exceptions. Why? Because putting it on the "one" side would mean storing multiple values in a single field — database suicide.
Data Insight
One-to-many relationships handle 80% of all business data connections. Master this pattern and you control most database design challenges.
The Foreign Key Foundation
A foreign key creates the bridge between tables. It's a column that references the primary key of another table. Think of it as a permanent address — it tells the database exactly where to find related information.
BookVault's order system demonstrates this perfectly. The orders table needs a customer_id foreign key pointing to the customers table. Each order belongs to exactly one customer, but customers can have multiple orders.
first_name
last_name
city
created_at
FK customer_id
order_date
status
total_amount
Creating the Foreign Key
-- Create customers table first (parent table)
-- BookVault customer data
CREATE TABLE customers (
customer_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
city VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Table 'customers' created successfully
What just happened?
We created the parent table first — this is mandatory. The customer_id acts as the primary key that other tables will reference. Try this: always create parent tables before child tables to avoid foreign key errors.
-- Create orders table with foreign key constraint
-- BookVault order tracking system
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
total_amount DECIMAL(10,2) NOT NULL,
-- Foreign key constraint ensures data integrity
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
Table 'orders' created successfully Foreign key constraint 'orders_ibfk_1' added
What just happened?
The FOREIGN KEY constraint creates the relationship. Now the database automatically prevents orphaned orders — you can't create an order for a customer that doesn't exist. Try this: attempt to insert an order with customer_id = 999 and watch it fail.
Cardinality and Participation
Cardinality describes how many instances can exist on each side of the relationship. In one-to-many, the "one" side has exactly one record, while the "many" side can have zero, one, or multiple records.
Participation determines whether the relationship is mandatory or optional. Can a customer exist without orders? Can an order exist without a customer? These business rules shape your database design.
Total Participation
Every order MUST have a customer. Orders cannot exist without a customer relationship.
Partial Participation
Customers can exist without orders. New customers haven't placed orders yet.
Business Logic
Cardinality enforces business rules. One author writes many books, but each book has one primary author.
Common Mistake
Putting foreign keys on the "one" side creates data redundancy and update anomalies.
Multiple One-to-Many Relationships
Real tables participate in multiple one-to-many relationships simultaneously. BookVault's books table connects to both authors and categories tables. Each book has one author and belongs to one category, but authors write many books and categories contain many books.
first_name
last_name
nationality
FK author_id
FK category_id
title
price
stock_qty
name
parent_category_id
Building the Complete Schema
-- Create parent tables first
-- BookVault author management
CREATE TABLE authors (
author_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
nationality VARCHAR(50)
);
-- BookVault category hierarchy
CREATE TABLE categories (
category_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
parent_category_id INT,
FOREIGN KEY (parent_category_id) REFERENCES categories(category_id)
);
Table 'authors' created successfully Table 'categories' created successfully Self-referencing foreign key constraint added
-- Create books table with multiple foreign keys
-- BookVault product catalog
CREATE TABLE books (
book_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
author_id INT NOT NULL,
category_id INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock_qty INT DEFAULT 0,
published_year YEAR,
-- Multiple foreign key constraints
FOREIGN KEY (author_id) REFERENCES authors(author_id),
FOREIGN KEY (category_id) REFERENCES categories(category_id)
);
Table 'books' created successfully Foreign key constraints 'books_ibfk_1' and 'books_ibfk_2' added
What just happened?
The books table now has two foreign keys, creating two separate one-to-many relationships. Notice how categories also has a self-referencing foreign key for hierarchical categories. Try this: create "Fiction" and "Science Fiction" categories with the parent-child relationship.
Referential Integrity Actions
What happens when you delete a customer who has orders? Or update an author's ID? Referential integrity actions define how the database handles these scenarios automatically.
Without proper actions, you risk orphaned records — orders pointing to deleted customers, books referencing non-existent authors. Netflix learned this lesson the hard way when content removal broke recommendation algorithms.
| Action | Behavior | Use Case |
|---|---|---|
CASCADE |
Delete/update child records automatically | Delete customer → delete all their orders |
SET NULL |
Set foreign key to NULL | Delete author → set books.author_id to NULL |
RESTRICT |
Prevent deletion/update if children exist | Can't delete customer with pending orders |
SET DEFAULT |
Set foreign key to default value | Delete category → assign books to "General" |
-- Create orders with CASCADE delete
-- BookVault order management with referential integrity
CREATE TABLE orders_with_cascade (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
total_amount DECIMAL(10,2) NOT NULL,
-- CASCADE: delete orders when customer is deleted
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
Table 'orders_with_cascade' created successfully Foreign key constraint with CASCADE actions added
Warning: CASCADE Can Be Dangerous
CASCADE deletes can eliminate massive amounts of data instantly. Deleting one customer might cascade through orders, order_items, and reviews. Test cascade behavior thoroughly in development before deploying to production.
Querying One-to-Many Data
One-to-many relationships unlock powerful query patterns. You can find all orders for a customer, count books per author, or calculate total sales by category. The foreign key creates the bridge for JOIN operations.
-- Sample data for BookVault demo
INSERT INTO customers (first_name, last_name, email, city)
VALUES
('Sarah', 'Chen', 'sarah.chen@email.com', 'Seattle'),
('Marcus', 'Johnson', 'marcus.j@email.com', 'Portland'),
('Elena', 'Rodriguez', 'elena.r@email.com', 'San Francisco');
INSERT INTO authors (first_name, last_name, nationality)
VALUES
('Neil', 'Gaiman', 'British'),
('Ursula', 'Le Guin', 'American'),
('Haruki', 'Murakami', 'Japanese');
INSERT INTO categories (name)
VALUES
('Fantasy'),
('Science Fiction'),
('Literary Fiction');
3 customers inserted 3 authors inserted 3 categories inserted
-- Find all orders for a specific customer
-- BookVault customer order history
SELECT
c.first_name,
c.last_name,
o.order_id,
o.order_date,
o.total_amount,
o.status
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE c.email = 'sarah.chen@email.com'
ORDER BY o.order_date DESC;
first_name | last_name | order_id | order_date | total_amount | status -----------|-----------|----------|------------|--------------|-------- Sarah | Chen | 1001 | 2024-01-15 | 89.97 | shipped Sarah | Chen | 1003 | 2024-01-10 | 24.99 | delivered
-- Count books per author (aggregation across one-to-many)
-- BookVault author productivity analysis
SELECT
a.first_name,
a.last_name,
a.nationality,
COUNT(b.book_id) as total_books,
AVG(b.price) as avg_book_price
FROM authors a
LEFT JOIN books b ON a.author_id = b.author_id
GROUP BY a.author_id, a.first_name, a.last_name, a.nationality
ORDER BY total_books DESC;
first_name | last_name | nationality | total_books | avg_book_price -----------|-----------|-------------|-------------|---------------- Neil | Gaiman | British | 8 | 16.99 Ursula | Le Guin | American | 12 | 14.50 Haruki | Murakami | Japanese | 6 | 18.75
What just happened?
The LEFT JOIN preserves all authors, even those without books. The GROUP BY collapses multiple books per author into summary statistics. Notice how COUNT(b.book_id) counts actual books, not NULLs for authors without books. Try this: change to INNER JOIN and see authors without books disappear.
Pro Tip: Use LEFT JOIN when you want to preserve all records from the "one" side, even if they have no related records on the "many" side. Use INNER JOIN when you only want records that have relationships.
Quiz
1. In BookVault's customer-orders relationship, where should the foreign key be placed?
2. If a BookVault customer is deleted and the foreign key has ON DELETE CASCADE, what happens to their orders?
3. To display all BookVault authors and count their books (including authors with zero books), which JOIN type should you use?
Up Next
Many-to-Many Relationships
Handle complex relationships where both sides can have multiple connections, using junction tables to model BookVault's book-author collaborations and customer wishlists.