Skip to content

Intermediate: combining and changing data · JOINs · topic 21 of 62 · 35 min

Why joins, and INNER JOIN

Explanation

A well-designed database splits data into several tables and links them with keys, so each fact is stored once. A JOIN puts the pieces back together for one query by matching rows on those keys. INNER JOIN keeps only the rows that find a partner on the other side.

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.

Think of it like this: Think of a railway reservation chart and the passenger list. The chart says "seat 23 → passenger P-4417". The passenger list says "P-4417 → Ravi Kumar, 34, Delhi". To read "seat 23: Ravi Kumar", you match the two on the passenger number. A join does that matching for every row, in milliseconds.

The INNER JOIN syntax

sql
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 order
JOIN and INNER JOIN mean the same thing. ON gives the matching rule: the order's customer_id must equal the customer's id.

Read 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.

sql
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 missing
NULL = 1 is not true, so a NULL key never matches anything. The next topic shows LEFT JOIN, which keeps him.

The 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.

sql
SELECT o.id, c.name
FROM orders o, customers c
WHERE c.id = o.customer_id;   -- same 8 rows as the JOIN above
Forget the WHERE line and you get every customer paired with every order: 6 × 8 = 48 rows of nonsense.

Prefer 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

JOIN: combining tables

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.

sql
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;
Asha Verma's 5 order lines: rice and dal in 101, kurtas and a tiffin box in 103, rice in 108.

Practice (0/4 done)

Task 1 · Code · SQL

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.

Task 2 · Code · SQL

Show every order line with the product name instead of the product id. Return order_id, the product name and qty.

Task 3 · Code · SQL

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.

Task 4 · Code · SQL

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

Question 1

1.Why does an order row store customer_id instead of the customer's name and city?

Question 2

2.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)?

Question 3

3.What does SELECT name FROM employees e JOIN departments d ON d.id = e.dept_id; give in SQLite?

Question 4

4.FROM customers c, orders o with no WHERE: SHOP has 6 customers and 8 orders. How many rows?

Question 5

5.Asha has three orders. In orders JOIN customers, how many result rows mention her?

Read more (free)

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.

From Joins: combining tables

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.

From Join pitfalls and how to avoid 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.

From LEFT, RIGHT and FULL OUTER JOIN

A table of 5 stores is cross joined with a table of 30 dates. How many rows?Related

Answer: 150

Every store with every date: 5 × 30.

From Self joins and cross joins

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

  1. 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.
  2. Module 5 exam: JOINs · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Subqueries, CTEs and set operations. 62 topics left in the course before the final exam.

← Previous: Writing reports with aggregation