Kodewick
Lesson 11 of 21Contents

Joining two tables

Lesson 11 of 21 · Part 3 · More than one table · about 15 minutes

The orders table records which customer placed each order, but only by the customer's number:

main.sql
1SELECT * FROM orders ORDER BY id;
Output
idcustomer_idorder_datestatus
112025-09-02shipped
222025-09-05shipped
312025-09-18shipped
442025-09-21cancelled
552025-10-01shipped
632025-10-03shipped
722025-10-10pending
872025-10-12shipped
912025-10-15pending
1082025-10-20shipped
1152025-10-22shipped
1242025-10-25pending
12 rows

Order 4 was placed by the customer whose customer_id is 4. What is that customer's name, and which city do they live in? For that you have to look in the customers table. This lesson is about using two tables in one query.

Why several tables?

Couldn't each order just hold the customer's name and city? It could, but customer 1 has placed 3 orders, so her name and city would be written 3 times. If she moves one day, all 3 places have to change; miss one and the data contradicts itself.

So databases usually store it like this: a customer's details are written once, in the customers table, and an order holds only the customer's number. When you need both, you join the two tables by that number.

JOIN … ON

FROM table1 JOIN table2 ON condition pairs up the rows of the two tables, and ON says how to pair them. Here, the order's customer_id equals the customer's id:

main.sql
1SELECT orders.id, orders.order_date, customers.name2FROM orders3JOIN customers ON orders.customer_id = customers.id4ORDER BY orders.id;
Output
idorder_datename
12025-09-02Ana Silva
22025-09-05Ben Okafor
32025-09-18Ana Silva
42025-09-21David Kim
52025-10-01Emma Novak
62025-10-03Chloe Martin
72025-10-10Ben Okafor
82025-10-12George Brown
92025-10-15Ana Silva
102025-10-20Hana Sato
112025-10-22Emma Novak
122025-10-25David Kim
12 rows

A column name both tables have (such as id) is written table.column, to say which table it comes from.

Press Step and look at the JOIN step: each order takes its customer_id to the customers table, finds the row with the same id, and the two are put together into one row. 12 orders make 12 rows.

JOIN can also be written INNER JOIN; it means exactly the same.

Table aliases

Writing the table names out every time gets long. Give each table a short name in FROM and JOIN, and use the short name from then on:

main.sql
1SELECT o.id, o.order_date, c.name, c.city2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4ORDER BY o.id;
Output
idorder_datenamecity
12025-09-02Ana SilvaLondon
22025-09-05Ben OkaforParis
32025-09-18Ana SilvaLondon
42025-09-21David KimTokyo
52025-10-01Emma NovakBerlin
62025-10-03Chloe MartinLondon
72025-10-10Ben OkaforParis
82025-10-12George BrownTokyo
92025-10-15Ana SilvaLondon
102025-10-20Hana SatoLondon
112025-10-22Emma NovakBerlin
122025-10-25David KimTokyo
12 rows

orders AS o means "the orders table, called o from here on". The AS can be left out: orders o means the same.

Columns with the same name

A column that only one table has, such as order_date or name, can go without the table name. A column both tables have can't:

main.sql
1SELECT id, name, order_date2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id;
Output
ERROR 1052 (23000): Column 'id' in field list is ambiguous

ERROR 1052: column id is "ambiguous". The orders table has an id, and so does the customers table, and the database doesn't know which one you mean. Write o.id or c.id.

Tip: In a join, put the table alias on every column (o.id, c.name), even where leaving it out would work. Anyone reading can see at a glance where each column comes from.

Unmatched rows are gone

Now the other way round, from the customers to their orders:

main.sql
1SELECT c.id, c.name, o.id AS order_id2FROM customers AS c3JOIN orders AS o ON o.customer_id = c.id4ORDER BY c.id, o.id;
Output
idnameorder_id
1Ana Silva1
1Ana Silva3
1Ana Silva9
2Ben Okafor2
2Ben Okafor7
3Chloe Martin6
4David Kim4
4David Kim12
5Emma Novak5
5Emma Novak11
7George Brown8
8Hana Sato10
12 rows

Press Step again and watch the pairing: customer 1 matches 3 orders and becomes 3 rows; customers 6, 9 and 10 have no orders at all. No match, no row: they aren't in the result at all.

JOIN keeps only rows that found a partner on both sides. To keep the customers who never ordered as well, you need lesson 12's LEFT JOIN.

Guess first

The products table has 10 rows, order_items has 18, and nobody has ever bought the desk lamp (product 6). How many rows does the result have?

1SELECT p.name, oi.order_id2FROM products AS p3JOIN order_items AS oi ON oi.product_id = p.id4ORDER BY oi.order_id, p.id;

JOIN with WHERE

A joined result is like any table: WHERE and ORDER BY work on it as usual. Which orders did customers in London place?

main.sql
1SELECT o.id, o.order_date, c.name2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4WHERE c.city = 'London'5ORDER BY o.id;
Output
idorder_datename
12025-09-02Ana Silva
32025-09-18Ana Silva
62025-10-03Chloe Martin
92025-10-15Ana Silva
102025-10-20Hana Sato
5 rows

city is in the customers table and order_date in the orders table; after the join, each row has both, so WHERE can use a column from either table.

What's in an order

order_items has only product numbers. Join the products table and you can see what order 3 contains:

main.sql
1SELECT p.name, oi.quantity, oi.unit_price2FROM order_items AS oi3JOIN products AS p ON oi.product_id = p.id4WHERE oi.order_id = 35ORDER BY p.id;
Output
namequantityunit_price
Headphones159.00
USB cable28.50
2 rows

Joining three tables at once (orders → order lines → products) comes in lesson 13.

Practice

Write the query, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

Pending orders

Get all pending orders: the order's id and order_date, and the name of the customer who placed it, sorted by order id.

It should print
idorder_datename
72025-10-10Ben Okafor
92025-10-15Ana Silva
122025-10-25David Kim
3 rows
exercise1.sql
1SELECT id, order_date, customer_id2FROM orders3WHERE status = 'pending';
Output
Press Run or Ctrl+Enter
Exercise 2

Electronics sold

From the order lines, get every line for an electronics product: order_id, the product's name, and quantity, sorted by order_id, and within an order by product number.

It should print
order_idnamequantity
3Headphones1
3USB cable2
4Phone case1
8Headphones1
10USB cable1
10Phone case1
6 rows
exercise2.sql
1SELECT order_id, product_id, quantity FROM order_items;
Output
Press Run or Ctrl+Enter
Exercise 3

Fix it: a shared column name

This should get the orders placed by customers in Tokyo: the order's id, the customer's name, and order_date, sorted by order id. But it gives ERROR 1052. Fix it.

It should print
idnameorder_date
4David Kim2025-09-21
8George Brown2025-10-12
12David Kim2025-10-25
3 rows
exercise3.sql
1SELECT id, name, order_date2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4WHERE city = 'Tokyo'5ORDER BY id;
Output
Press Run or Ctrl+Enter

What you learned

  • Data is split across tables so that each fact is written once; the tables are linked by id numbers.
  • FROM table1 JOIN table2 ON condition puts matching rows together into one; JOIN is the same as INNER JOIN.
  • Give tables aliases (orders AS o) and write columns as o.id; a column both tables have, without a table name, gives ERROR 1052.
  • Rows with no match don't appear in the result; to keep them, use lesson 12's LEFT JOIN.
  • A joined result works with WHERE and ORDER BY as usual.