Intermediate: combining and changing data · JOINs · topic 21 of 62 · 35 min
Why joins, and INNER JOIN
Explanation
Why is the data split up?
In module 1 you met the SHOP tables. An order row does not hold the customer's name and city; it holds only customer_id, a foreign key that points to a row in customers. This is called normalisation: each fact (Asha lives in Pune) is stored in exactly one place. If Asha moves to Mumbai, you change one row, not every order she ever placed.
The price of that tidiness is that a question like "show each order with the customer's name" needs two tables. A join is how SQL answers it. It takes rows from two tables and pairs them up wherever a condition, usually "this key equals that key", is true.
The INNER JOIN syntax
SELECT o.id, o.order_date, c.name
FROM orders AS o
JOIN customers AS c ON c.id = o.customer_id;
-- 101 | 2025-01-05 | Asha Verma
-- 102 | 2025-01-09 | Ravi Kumar
-- 103 | 2025-02-14 | Asha Verma
-- ... 8 rows, one per orderRead it as: for every order, find the customer whose id equals the order's customer_id, and put the two rows side by side. Asha appears three times because she has three orders. Each row of the result is one matching pair.
Table aliases
orders AS o gives the table a short nickname, a table alias, for this query. Then o.id means "the id column of orders" and c.id means "the id column of customers". Both tables have a column called id, so without the prefix the database would not know which you mean. Use short, meaningful aliases (o for orders, c for customers, oi for order_items), and prefix every column once two or more tables are involved.
INNER JOIN keeps matches only
A row with no partner on the other side is dropped. In COMPANY, Deepak Yadav has dept_id NULL. When you join employees to departments, his row finds no department, so he disappears from the result. Legal has no employees, so it disappears too. Ten of the eleven employees come back.
SELECT e.name, d.name AS department
FROM employees AS e
JOIN departments AS d ON d.id = e.dept_id;
-- Meera Iyer | Engineering
-- ...
-- Lakshmi Pillai | HR
-- 10 rows: Deepak Yadav (no department) is missingThe old comma syntax
Before 1992, joins were written by listing the tables with commas and putting the matching rule in WHERE. It still works in every database, and you will see it in old code.
SELECT o.id, c.name
FROM orders o, customers c
WHERE c.id = o.customer_id; -- same 8 rows as the JOIN abovePrefer JOIN ... ON. It keeps the matching rule next to the table it belongs to, and leaves WHERE for real filters such as status = 'Delivered'.
A caveat about Venn diagrams
Joins are often drawn as two overlapping circles. That picture helps you remember which side is kept, but it is misleading in one way: a join does not return a set of keys, it returns pairs of rows. If one customer has three orders, the result has three rows for her. A join can return more rows than either table has. Keep that in mind; topic 5 shows how it inflates totals.
Common mistakes
- Joining on the wrong columns, such as ON c.id = o.id. It runs without an error and gives wrong pairs. Always join a foreign key to the key it points to.
- Leaving out the table prefix: SELECT name FROM employees e JOIN departments d ... fails with "ambiguous column name: name". Write e.name or d.name.
- Using the comma syntax and forgetting the WHERE, which pairs every row with every row. Use JOIN ... ON.
- Being surprised that rows vanished. INNER JOIN drops rows with no partner; check for NULL or missing keys, or use a LEFT JOIN.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Why joins, and INNER JOIN · 1/7
Real databases split data into many tables. Today we learn to put the pieces back together with a join.
Prefer a video by a teacher? Find free videos on “sql inner join tutorial table alias beginners” on YouTube ↗
In real life: An order history screen
When you open "My Orders" on a shopping app, the screen shows the date, the status and the product names. Those come from at least three tables: orders, order_items and products. The server runs a join filtered to your customer id, so the product name is always the current one from the products table and is never copied into millions of order rows.
SELECT o.id, o.order_date, o.status, p.name AS product, oi.qty
FROM orders AS o
JOIN order_items AS oi ON oi.order_id = o.id
JOIN products AS p ON p.id = oi.product_id
WHERE o.customer_id = 1;Practice (0/4 done)
Show every order with the name of the customer who placed it. Return three columns: the order id, the order_date and the customer name.
Show every order line with the product name instead of the product id. Return order_id, the product name and qty.
In COMPANY, show each employee with the name of their department. Return the employee name and the department name (call it department). Use an INNER JOIN. One employee will be missing: think about why.
List the Delivered orders with the customer's name and city, oldest order first. Return the order id, the customer name and the city.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- LEFT, RIGHT and FULL OUTER JOINJOINs · same module
- Joining three or more tablesJOINs · same module
- Self joins and cross joinsJOINs · same module
- Joins: combining tablesBackend Development · SQL in depth
- Correlated subqueries and derived tablesSubqueries, CTEs and set operations
- IN, EXISTS and NOT EXISTSSubqueries, CTEs and set operations
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why does an order row store customer_id instead of the customer's name and city?
Answer: So each fact about a customer is stored once and changed in one place
This is normalisation: no copies to keep in sync.
Employees has 11 rows; one has dept_id NULL. How many rows does employees INNER JOIN departments ON d.id = e.dept_id return (every other employee has a valid department)?
Answer: 10
The NULL key matches nothing, so that employee is dropped.
What does SELECT name FROM employees e JOIN departments d ON d.id = e.dept_id; give in SQLite?
Answer: An error: ambiguous column name
Both tables have a name column; prefix it with e. or d.
FROM customers c, orders o with no WHERE: SHOP has 6 customers and 8 orders. How many rows?
Answer: 48
Without a matching rule, every row pairs with every row: 6 × 8.
Asha has three orders. In orders JOIN customers, how many result rows mention her?
Answer: Three, one per matching order
A join returns one row per matching pair, not one per key.
Customers has 7 rows; 2 of them have no orders. What does an INNER JOIN of customers with orders do with those 2 customers?Related
Answer: Leaves them out
INNER JOIN keeps only rows that have a match on both sides. A LEFT JOIN would keep them with NULLs.
customers LEFT JOIN orders ... WHERE o.status = 'Delivered'. What happens to customers with no Delivered orders?Related
Answer: They are removed, because NULL = 'Delivered' is not true
Move the condition into ON to keep them.
customers LEFT JOIN orders: what appears for a customer with no orders?Related
Answer: One row with the customer's columns and NULL in every order column
LEFT JOIN keeps every left row and fills the missing side with NULL.
A table of 5 stores is cross joined with a table of 30 dates. How many rows?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 4 more topics in “JOINs” before its exam: LEFT, RIGHT and FULL OUTER JOIN, Joining three or more tables, Self joins and cross joins, Join pitfalls and how to avoid them.
- Module 5 exam: JOINs · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Subqueries, CTEs and set operations. 62 topics left in the course before the final exam.