INNER JOIN
INNER JOIN is the most common join.
It returns rows where the join condition matches in both tables.
Core Concepts
flowchart TD
A[Left table row] --> B{Match in right table?}
B -->|Yes| C[Return combined row]
B -->|No| D[Exclude row]
| Piece | Example |
|---|---|
| Left table | customers_join c |
| Right table | orders_join o |
| Predicate | c.customer_id = o.customer_id |
Code Examples
-- Return only customers who have orders.
SELECT
c.customer_id,
c.name,
o.order_id,
o.total
FROM customers_join AS c
INNER JOIN orders_join AS o
ON c.customer_id = o.customer_id
ORDER BY c.customer_id, o.order_id;
| customer_id | name | order_id | total |
|---|---|---|---|
| 1 | Ada | 101 | 49.99 |
| 1 | Ada | 102 | 19.50 |
| 2 | Alan | 103 | 75.00 |
-- Aggregate after inner join: order count per matched customer.
SELECT c.name, count(*) AS order_count
FROM customers_join AS c
INNER JOIN orders_join AS o
ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY c.customer_id;
| name | order_count |
|---|---|
| Ada | 2 |
| Alan | 1 |
SQLite-Specific Nuances
SQLite Nuance
In SQLite, writing JOIN without a qualifier means INNER JOIN.
Common Pitfalls / Best Practices
Pitfall
Using INNER JOIN when you actually need rows with no matches (for example, customers with zero orders).
Best Practice
Choose join type based on business question first, then write SQL.
Quick Challenge
Return only name and total for matched customer-order rows.
View Solution
SELECT c.name, o.total
FROM customers_join AS c
INNER JOIN orders_join AS o
ON c.customer_id = o.customer_id
ORDER BY c.name, o.total;
| name | total |
|---|---|
| Ada | 19.50 |
| Ada | 49.99 |
| Alan | 75.00 |