SQL Joins — All Types & Multi-Table
Joins are the most-tested topic in any SQL interview. They combine data from multiple tables — and reveal whether you think in sets or guess at syntax.
Learning Objectives
- Explain what a JOIN does and why real data lives across many tables.
- Use
INNER JOIN,LEFT JOIN,RIGHT JOIN, andFULL OUTER JOINcorrectly. - Find rows with no match using an anti-join (
LEFT JOIN ... WHERE key IS NULL). - Write a self join (with aliases) and a cross join.
- Understand the critical
ONvsWHEREtrap in outer joins. - Chain joins across 3+ tables and explain JOIN vs UNION.
0. The Sample Tables
Joins need at least two related tables. We use customers ↔ transactions (from earlier), plus a new employees table for self joins.
| customer_id | customer_name | city | account_type | balance |
|---|---|---|---|---|
| 1 | Aarav Sharma | Mumbai | Savings | 25000 |
| 2 | Priya Verma | Delhi | Current | 120000 |
| 3 | Rahul Mehta | Mumbai | Savings | 8000 |
| 4 | Sneha Iyer | Bengaluru | Salary | 95000 |
| 5 | Vikram Singh | Delhi | Current | 45000 |
| 6 | Ananya Das | Chennai | Savings | 15000 |
| txn_id | customer_id | amount |
|---|---|---|
| 101 | 1 | 500 |
| 102 | 1 | 1200 |
| 103 | 2 | 800 |
| 104 | 3 | 300 |
| 105 | 1 | 950 |
| 106 | 4 | 1500 |
| emp_id | emp_name | manager_id | dept |
|---|---|---|---|
| 1 | Aarav Mehta | NULL | Leadership |
| 2 | Priya Rao | 1 | Finance |
| 3 | Rahul Shah | 1 | Marketing |
| 4 | Sneha Iyer | 2 | Finance |
| 5 | Vikram Das | 2 | Finance |
customers.customer_id ↔ transactions.customer_id is the relationship (a foreign key).
1. INNER JOIN — Only Matching Rows
INNER JOIN returns only rows where there's a match in both tables.
Rows without a match on either side are dropped.
-- Customers who have at least one transaction
SELECT c.customer_name, t.amount
FROM customers c
INNER JOIN transactions t
ON c.customer_id = t.customer_id;
Customers 5 and 6 have no transactions, so they don't appear. Customer
1 appears three times (once per transaction).
2. LEFT JOIN — Keep All Left Rows
LEFT JOIN keeps all rows from the left (first) table, and matches from the
right table where possible. Unmatched right columns become NULL.
-- ALL customers, with their transactions (NULL if none)
SELECT c.customer_name, t.amount
FROM customers c
LEFT JOIN transactions t
ON c.customer_id = t.customer_id;
Now customers 5 and 6 appear with NULL amounts — because we kept the entire left table.
3. RIGHT JOIN and FULL OUTER JOIN
RIGHT JOIN — mirror of LEFT JOIN
Keeps all rows from the right table. Any RIGHT JOIN can be rewritten as a LEFT JOIN by swapping the tables.
-- These two are identical:
SELECT c.customer_name, t.amount
FROM customers c
RIGHT JOIN transactions t ON c.customer_id = t.customer_id;
SELECT c.customer_name, t.amount
FROM transactions t
LEFT JOIN customers c ON t.customer_id = c.customer_id;
FULL OUTER JOIN — keep everything from both sides
Returns all rows from both tables, matching where possible and filling with NULL where not.
SELECT c.customer_name, t.amount
FROM customers c
FULL OUTER JOIN transactions t
ON c.customer_id = t.customer_id;
LEFT JOIN because it reads in the
same direction as the FROM clause. RIGHT JOIN is rare and mostly appears to test
whether you understand the symmetry.
4. Finding Unmatched Rows (Anti-Join)
To find rows in one table with no match in another, use a LEFT JOIN and filter
WHERE the right table's key IS NULL. This is one of the most-asked join questions.
-- Customers who have NEVER made a transaction
SELECT c.customer_name
FROM customers c
LEFT JOIN transactions t
ON c.customer_id = t.customer_id
WHERE t.txn_id IS NULL;
Returns customers 5 and 6 — they were kept by the LEFT JOIN but found no transaction match.
NULL, so the right key being NULL precisely marks a non-match.
5. Self Join and Cross Join
Self join — a table joined to itself
Used when rows relate to other rows in the same table — e.g., employees and their managers.
-- Each employee with their manager's name
SELECT e.emp_name AS employee, m.emp_name AS manager
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.emp_id;
Using LEFT JOIN keeps the top boss (whose manager_id is NULL) in the result.
e, m),
the two copies of the table are indistinguishable, so column references become ambiguous.
Cross join — the Cartesian product
CROSS JOIN pairs every row of the first table with every row of the second.
-- 3 sizes × 4 colors = 12 combinations
SELECT s.size, c.color
FROM sizes s
CROSS JOIN colors c;
Useful for generating combinations. It also happens accidentally when you forget the ON condition.
6. The ON vs WHERE Trap (Critical)
For an outer join (LEFT/RIGHT), a filter on the right table behaves differently depending
on whether you put it in ON or WHERE.
-- Filter in ON: still keeps ALL customers (shows txn only for one type)
SELECT c.customer_name, t.amount
FROM customers c
LEFT JOIN transactions t
ON c.customer_id = t.customer_id
AND t.amount > 1000;
Customers with no matching >1000 transaction still appear, with NULL amount.
-- Filter in WHERE: removes NULL rows → effectively an INNER JOIN
SELECT c.customer_name, t.amount
FROM customers c
LEFT JOIN transactions t
ON c.customer_id = t.customer_id
WHERE t.amount > 1000;
Now customers with no >1000 transaction vanish — the WHERE kills the NULL rows the LEFT JOIN created.
ON preserves unmatched left rows; the same condition in WHERE turns a
LEFT JOIN into an INNER JOIN. Always ask yourself where the filter should live.
7. Multi-Table Joins and JOIN vs UNION
Joining more than two tables
Chain joins one after another, each with its own ON. The result of the first join feeds the next.
SELECT e.emp_name, d.dept_name, l.city
FROM employees e
JOIN departments d ON e.dept_id = d.id
JOIN locations l ON d.location_id = l.id;
JOIN vs UNION
| JOIN | UNION | |
|---|---|---|
| Direction | horizontal (adds columns) | vertical (adds rows) |
| Purpose | combine related tables side by side | stack results of two queries |
| Requirement | a related key | same number/type of columns |
8. Interview Questions (with Model Answers)
Joins are the most-tested SQL topic. These are the questions interviewers reuse constantly. Self-test before revealing each answer.
IQ1. What is the difference between INNER JOIN and OUTER JOIN?
Model answer: "INNER JOIN returns only rows with a match in both tables. OUTER JOIN — LEFT, RIGHT, or FULL — keeps unmatched rows from one or both tables and fills the missing side with NULL. I'd use an outer join when 'no match' is a result I care about."
IQ2. What's the real difference between LEFT JOIN and RIGHT JOIN?
Model answer: "LEFT JOIN keeps all rows from the left (first) table; RIGHT JOIN keeps all from the right (second). They're mirror images — any RIGHT JOIN can be rewritten as a LEFT JOIN by swapping the table order. Most analysts standardize on LEFT JOIN."
IQ3. How do you find rows in one table with no match in another?
Model answer: "I use a LEFT JOIN and filter for the right table's key being NULL —
LEFT JOIN ... WHERE right.key IS NULL. This keeps all left rows but only shows those that found
no match." This is one of the most-asked join questions.
IQ4. What is a self join, and why do you need aliases?
Model answer: "A self join joins a table to itself — like employees to their managers. I need aliases because otherwise the two copies of the table are indistinguishable and column references become ambiguous."
IQ5. What is a CROSS JOIN?
Model answer: "It returns the Cartesian product — every row of one table paired with every row of the other. If one table has 4 rows and the other 3, you get 12 rows. It's useful for combinations, but it also happens accidentally when you forget the ON condition."
IQ6. Does a filter in ON behave the same as in WHERE?
Model answer: "For a LEFT JOIN, no. A condition on the right table in ON still
keeps unmatched left rows as NULLs, but the same condition in WHERE removes those NULL rows and
effectively turns the LEFT JOIN into an INNER JOIN. This is the classic trap."
IQ7. Can you join more than two tables? How?
Model answer: "Yes — I chain joins one after another, each with its own ON condition. The result of the first join becomes the input to the next. For inner joins the order doesn't change the final result, but for outer joins it can affect which rows are preserved."
IQ8. What's the difference between JOIN and UNION?
Model answer: "JOIN combines columns from two tables side by side (horizontal — more columns per row). UNION stacks rows of two queries on top of each other (vertical — more rows), and requires the same columns. They solve completely different problems."
IQ9. How do NULLs behave in a join condition?
Model answer: "A condition like a.id = b.id never matches when either side is
NULL, because NULL = NULL is unknown, not true. So NULL keys never join and always end up as unmatched rows."
Hands-On Project: Join the Customers and Transactions
Using customers, transactions, and employees, write a query for each task.
Steps
- List customer names with their transaction amounts (only customers who have transactions).
- List ALL customers with their transaction amounts (NULL where none).
- List customers who have never made a transaction.
- List total transaction amount per customer (using a join + GROUP BY).
- List each employee with their manager's name.
View Solution / Walkthrough
-- 1. INNER JOIN (customers with transactions)
SELECT c.customer_name, t.amount
FROM customers c
INNER JOIN transactions t ON c.customer_id = t.customer_id;
-- 2. LEFT JOIN (all customers)
SELECT c.customer_name, t.amount
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id;
-- 3. Anti-join (customers with no transactions)
SELECT c.customer_name
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id
WHERE t.txn_id IS NULL;
-- → Vikram Singh, Ananya Das
-- 4. Total transaction amount per customer
SELECT c.customer_name, COALESCE(SUM(t.amount), 0) AS total_spent
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id
GROUP BY c.customer_name
ORDER BY total_spent DESC;
-- 5. Self join (employees + managers)
SELECT e.emp_name AS employee, m.emp_name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;
Key Takeaways
INNER JOIN keeps only matches; LEFT JOIN keeps all left rows and NULLs the rest.
The anti-join LEFT JOIN ... WHERE key IS NULL finds rows with no match.
In outer joins, a filter in ON vs WHERE changes the result — the classic trap.
Self joins need aliases; cross joins multiply every row with every row.
JOIN is horizontal (columns), UNION is vertical (rows). NULL keys never join.
Objective Questions — Test Your Understanding
Q1. Which join returns only rows with a match in both tables?
Q2. Which join keeps ALL rows from the left table, filling unmatched right columns with NULL?
Q3. How do you find customers who have made NO transactions?
Q4. In a LEFT JOIN, what value fills the right-table columns for unmatched rows?
Q5. Which join joins a table to itself (e.g., employees to their managers)?