INNER JOIN

RoadmapsSQL

Overview

INNER JOIN combines rows from two tables where a condition is true — typically matching a foreign key in one table to a primary key in another. (Note: writing just `JOIN` is shorthand for `INNER JOIN` in PostgreSQL). Rows that have no match in the other table are silently dropped from the result. Think of it as a filter across two tables: start with every possible pair of rows (the Cartesian product), then keep only the pairs where the ON condition is true. Against the shared dataset: SELECT c.name, o.id AS order_id FROM customers c INNER JOIN orders o ON o.customer_id = c.id ORDER BY c.name, o.id; This returns 8 rows — one for every order matched to its customer. Customers Greg and Hana appear nowhere because they have no orders; the join silently will exclude them. Mental model: INNER JOIN = 'give me only the rows that exist in BOTH tables'.

INNER JOIN is the most common join type. Almost every multi-table query starts here — listing orders with customer names, showing line items with product details, connecting any two related tables. Understanding that unmatched rows vanish is the key insight that separates it from outer joins.

Where used: order detail pages joining orders to products, analytics dashboards combining customers and orders, API endpoints returning enriched records

Why learn this

Query walkthrough

SELECT c.name, p.name AS product, oi.quantity
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id
INNER JOIN order_items oi ON oi.order_id = o.id
INNER JOIN products p ON p.id = oi.product_id
WHERE c.name = 'Alice'
ORDER BY o.id, p.name;

Focus: Watch four tables chain together: customers → orders → order_items → products. Each INNER JOIN narrows the result to only matching rows, and the WHERE further filters to Alice's rows only.

Common mistakes

Glossary

foreign
A column in one table that connects to the primary key in another table, creating a relationship between the two.
exclude
To deliberately leave out or remove items from a result, such as ignoring rows that do not have a match.
insight
A deep understanding or realization about how a specific concept or system works.

Recall questions

Understanding checks

Which customer names and statuses does this return?

Alice, paid Alice, paid Bob, paid Carlos, paid Emma, paid Farah, paid

There are 8 orders total; INNER JOIN matches all 8 to their customers. The WHERE clause then keeps only the 6 with status = 'paid', dropping Bob's pending order (order 3) and Diana's cancelled order (order 5).

What names and counts appear? Do Greg and Hana appear?

Alice, 2 Bob, 2 Carlos, 1 Diana, 1 Emma, 1 Farah, 1 Greg and Hana do NOT appear.

INNER JOIN drops customers with no matching orders. Greg and Hana have zero orders, so they never enter the joined row set and cannot be counted. To include them with a count of 0 you would need a LEFT JOIN with COUNT(o.id).

Practice tasks

Add product prices to the order items

This query lists order items with their product names. Modify it to also show each product's price by adding the missing column from the products table. Keep the existing JOIN and ordering.

Challenge

Total spend per customer

Write a query that shows each customer's name and their total spend (quantity × price across all their order items). Only include customers who have orders. Order by total spend descending, then by name ascending.

Continue learning

Previous: SELECT, FROM & Columns

Previous: WHERE & Operators

Next: LEFT / RIGHT JOIN

Next: FULL OUTER & CROSS JOIN

Next: Self Join

Return to SQL Roadmap