Joining Tables: INNER JOIN
Prerequisites: Relational databases: tables, rows, and keys, Grouping with GROUP BY
Data is split across tables to avoid repetition. A join combines them back together by matching related columns — usually a foreign key with the primary key it refers to.
Real-world analogy: a stack of receipts shows customer numbers; a customer list maps numbers to names. Joining them is like writing each customer's name onto their receipts.
Syntax
SELECT columns
FROM table_a
JOIN table_b ON table_a.column = table_b.column;
Example
SELECT orders.id, customers.name, orders.ordered_on, orders.status
FROM orders
JOIN customers ON customers.id = orders.customer_id;
Expected output
+----+-------------------+------------+-----------+
| id | name | ordered_on | status |
+----+-------------------+------------+-----------+
| 1 | Ada Lovelace | 2025-01-05 | delivered |
| 2 | Ada Lovelace | 2025-02-14 | delivered |
| 3 | Linus Torvalds | 2025-02-20 | shipped |
| 4 | Grace Hopper | 2025-03-01 | delivered |
| 5 | Grace Hopper | 2025-03-18 | cancelled |
| 6 | Alan Turing | 2025-04-02 | pending |
| 7 | Margaret Hamilton | 2025-04-10 | shipped |
| 8 | Ada Lovelace | 2025-04-22 | pending |
+----+-------------------+------------+-----------+
JOIN(short forINNER JOIN) pairs each order with the customer whoseidmatches the order'scustomer_id.ONgives the matching condition.- Columns are written as
table.column, because both tables have anidcolumn —idalone would be ambiguous.
Inner joins keep only matches
An inner join returns only rows that have a match in both tables. Tim Berners-Lee (customer 6) has no orders, so he doesn't appear. If an order referred to a customer ID that didn't exist, that order wouldn't appear either. The next lessons show how to keep unmatched rows.
Joining and filtering
SELECT customers.name, orders.id AS order_id, orders.status
FROM orders
JOIN customers ON customers.id = orders.customer_id
WHERE customers.city = 'London'
ORDER BY orders.id;
+--------------+----------+-----------+
| name | order_id | status |
+--------------+----------+-----------+
| Ada Lovelace | 1 | delivered |
| Ada Lovelace | 2 | delivered |
| Alan Turing | 6 | pending |
| Ada Lovelace | 8 | pending |
+--------------+----------+-----------+
Common mistakes
- Forgetting the
ONcondition (or getting it wrong), which pairs every row with every other row — a cross join that produces far too many rows. - Ambiguous column names:
SELECT id FROM orders JOIN customers ...fails withambiguous column name: id. Prefix columns with their table.
Practice
List each order item with the product's name: show order_id, the product
name, and quantity.
Show solution
SELECT order_items.order_id, products.name, order_items.quantity
FROM order_items
JOIN products ON products.id = order_items.product_id
ORDER BY order_items.order_id;
+----------+-----------------------------+----------+
| order_id | name | quantity |
+----------+-----------------------------+----------+
| 1 | Mechanical Keyboard | 1 |
| 1 | USB-C Hub | 2 |
| 2 | 27-inch Monitor | 1 |
| 3 | USB-C Hub | 1 |
| 3 | Noise-Cancelling Headphones | 1 |
| 4 | 27-inch Monitor | 2 |
| 4 | Laptop Stand | 2 |
| 5 | Webcam | 1 |
| 6 | Mechanical Keyboard | 1 |
| 7 | Laptop Stand | 1 |
| 7 | Noise-Cancelling Headphones | 1 |
| 8 | Webcam | 3 |
+----------+-----------------------------+----------+