Table Relationships

Design tables that link together correctly: one-to-one, one-to-many and many-to-many relationships, foreign keys and junction tables.

Lesson 17 of 102Intermediate17 min read

A database is more than a set of separate tables. Customers place orders, orders contain products, products have tags, students join courses. These connections are called relationships, and getting them right is the most important part of designing a database.

You've already used one relationship: each order belongs to one customer, stored as customer_id in the orders table. That's a one-to-many relationship. But not every connection fits that shape. A product can have many tags, and a tag can apply to many products. Where would you even put the column?

In this lesson you'll learn the three kinds of relationship, how each is built with foreign keys, why many-to-many needs an extra junction table, what should happen when a linked row is deleted, and how to query across these links.

One-to-many

One customer can have many orders, but each order belongs to one customer. Put the foreign key on the "many" side:

SQL
CREATE TABLE orders (
  id          INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL REFERENCES customers(id),
  order_date  DATE NOT NULL
);

This is the most common relationship by far: one author, many books; one category, many products; one post, many comments. Putting a list of order ids in a column of customers instead would be impossible to search or join properly.

Many-to-many and junction tables

A product can have many tags, and a tag can be on many products. Neither table can hold a single foreign key, so you add a third table whose only job is to record the pairs:

SQL
CREATE TABLE product_tags (
  product_id INTEGER NOT NULL REFERENCES products(id),
  tag_id     INTEGER NOT NULL REFERENCES tags(id),
  PRIMARY KEY (product_id, tag_id)
);

This is a junction table (also called a link, bridge or join table). Each row says "this product has this tag". The composite primary key on both columns stops the same pair being added twice. A junction table can hold extra facts about the pair too, such as the date a student enrolled on a course.

One-to-one

In a one-to-one relationship, each row matches at most one row in another table: a user and their profile settings, or an employee and their parking space. Use a foreign key that is also UNIQUE, or share the same primary key in both tables.

One-to-one tables are less common. They're used to keep rarely needed or sensitive columns separate, such as payroll details, or to split a very wide table. If two tables always appear together, a single table is usually simpler.

What happens on delete

When a customer is deleted, what should happen to their orders? The foreign key can say:

OptionEffect
ON DELETE RESTRICT (or NO ACTION)Refuse to delete a customer who has orders (the default)
ON DELETE CASCADEDelete their orders too
ON DELETE SET NULLKeep the orders, set customer_id to NULL

Choose based on meaning. Deleting a product should probably cascade to its rows in product_tags, but deleting a customer should rarely wipe their order history; RESTRICT is the safe choice there. In SQLite, foreign keys are only checked after PRAGMA foreign_keys = ON;.

SQL Relationships Syntax

Relationship patterns

-- one-to-many
child_table.parent_id INTEGER REFERENCES parent_table(id)

-- many-to-many
CREATE TABLE a_b (
  a_id INTEGER REFERENCES a(id) ON DELETE CASCADE,
  b_id INTEGER REFERENCES b(id) ON DELETE CASCADE,
  PRIMARY KEY (a_id, b_id)
);

SQL Relationships Summary

  • One-to-many: put the foreign key on the "many" side, such as customer_id in orders.
  • Many-to-many: add a junction table with two foreign keys and a composite primary key.
  • One-to-one: a foreign key that is also unique, used for optional or sensitive details.
  • ON DELETE RESTRICT, CASCADE or SET NULL decides what happens to linked rows.
  • Query a many-to-many relationship by joining through the junction table.

SQL Relationships Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Products with a given tag

The tags table holds 1 "back to school", 2 "eco" and 3 "gift idea", and product_tags links them to products.

SQL
SELECT p.name AS product, t.name AS tag
FROM products p
JOIN product_tags pt ON pt.product_id = p.id
JOIN tags t ON t.id = pt.tag_id
WHERE t.name = 'gift idea'
ORDER BY p.name;
Output
product      | tag
-------------+----------
Backpack     | gift idea
Desk lamp    | gift idea
Water bottle | gift idea
Line-by-line explanation
  1. Line 3Go from products into the junction table.
  2. Line 4Then from the junction table to tags.
  3. Line 5Filter on the tag's name. The junction table connects the two sides.
Example 2

Each product's tags on one line

SQL
SELECT p.name,
       group_concat(t.name, ', ') AS tags
FROM products p
JOIN product_tags pt ON pt.product_id = p.id
JOIN tags t ON t.id = pt.tag_id
GROUP BY p.id
ORDER BY p.id;
Output
name         | tags
-------------+--------------------------
Notebook     | back to school
Pen          | back to school
Backpack     | back to school, gift idea
Water bottle | eco, gift idea
Lunch box    | eco
Desk lamp    | gift idea
Line-by-line explanation
  1. Line 2group_concat joins the tag names of each group into one string. MySQL: GROUP_CONCAT(t.name SEPARATOR ', '). PostgreSQL: STRING_AGG(t.name, ', ').
  2. Line 6One row per product.
Example 3

Counting the "many" side

SQL
SELECT c.name, COUNT(o.id) AS orders
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id
ORDER BY c.id;
Output
name  | orders
------+-------
Ana   | 2
Ben   | 2
Chloe | 1
Dev   | 1
Ema   | 0
Line-by-line explanation
  1. Line 1COUNT(o.id) counts only real orders, so Ema gets 0 rather than 1.
  2. Line 3A LEFT JOIN keeps customers with no orders.

Common SQL Relationships Mistakes

A list of ids in one column

Don't
CREATE TABLE products (id INTEGER, name TEXT, tag_ids TEXT);
-- tag_ids = '1,3'  impossible to join or check
Do
CREATE TABLE product_tags (
  product_id INTEGER REFERENCES products(id),
  tag_id     INTEGER REFERENCES tags(id),
  PRIMARY KEY (product_id, tag_id)
);

The foreign key on the wrong side

Don't
ALTER TABLE customers ADD COLUMN order_id INTEGER;   -- only one order per customer
Do
-- orders.customer_id: each order points at its one customer

CASCADE where history matters

Don't
customer_id INTEGER REFERENCES customers(id) ON DELETE CASCADE
-- deleting a customer erases years of sales records
Do
customer_id INTEGER REFERENCES customers(id) ON DELETE RESTRICT

SQL Relationships in Real Projects

Relationships everywhere

Online shops link orders to customers (one-to-many), orders to products through order lines (many-to-many with extra columns such as quantity and price), and users to their settings (one-to-one). Social networks store follows and likes in huge junction tables, and school systems link students to courses with enrolment tables that also hold grades.

SQL Relationships FAQ

What is a junction table also called?

You'll see several names for the same idea: junction table, join table, bridge table, link table and associative table. Whatever the name, it holds pairs of foreign keys, such as product_id and tag_id, to represent a many-to-many relationship.

Should a junction table have its own id column?

Either design works. A composite primary key on the two foreign keys, PRIMARY KEY (product_id, tag_id), prevents duplicate pairs automatically. A separate id is convenient when the relationship has its own data and other tables refer to it; then add a UNIQUE constraint on the pair.

Can a table have a relationship with itself?

Yes. An employees table can have a manager_id column that references employees.id, and a categories table can have a parent_id for subcategories. Query one level with a self join, as in the advanced joins lesson, and whole trees with a recursive CTE.

What is cardinality in a database relationship?

Cardinality describes how many rows on one side can relate to rows on the other: one-to-one, one-to-many or many-to-many, plus whether the relationship is optional. It decides where the foreign key goes and whether you need a junction table.

How are relationships drawn in diagrams?

Entity-relationship diagrams show tables as boxes and relationships as lines. The common "crow's foot" notation draws a three-pronged foot on the "many" end and marks whether each side is optional. The ER diagrams lesson explains how to read and draw them.

SQL Relationships Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the junction table's composite primary key.

CREATE TABLE product_tags (
  product_id INTEGER REFERENCES products(id),
  tag_id     INTEGER REFERENCES tags(id),
  PRIMARY KEY ()
);
Multiple choiceeasy

In a one-to-many relationship, where does the foreign key go?

Multiple choicemedium

Students can take many courses, and courses have many students. What do you need?

Multiple choicemedium

What does ON DELETE CASCADE do?

Predict the outputhard

Using the tag data in this lesson, how many rows does SELECT * FROM product_tags WHERE tag_id = 1; return?

SQL Relationships Quiz

5 questions to check what you learned.

  1. Which relationship is "one customer, many orders"?

    • One-to-one
    • Many-to-many
    • Self join
    • One-to-many
  2. What stops the same product and tag being linked twice in a junction table?

    • A composite primary key (or UNIQUE constraint) on both columns
    • An index on product_id
    • ON DELETE CASCADE
    • NOT NULL
  3. What's the usual default behaviour when you delete a row that other rows reference?

    • The other rows are deleted
    • The references become NULL
    • The delete is refused
    • Nothing is checked
  4. What's another name for a junction table?

    • View
    • Link or bridge table
    • Index
    • Temporary table
  5. What does an entity relationship diagram show?

    • Query speed
    • Rows of data
    • User permissions
    • Tables and the relationships between them