INNER JOIN, LEFT JOIN, FULL OUTER JOIN, self-joins, and multi-table queries — the most feared concept made simple.
F-5 — Connecting Tables — Joins Demystified
Who this module is for: You completed F-4 and understand aggregation. Now you need to combine data from multiple tables — the foundational skill that makes relational databases genuinely powerful. Joins confuse almost every beginner because they require a new mental model. This module builds that model step by step.
Why Data Lives in Multiple Tables
Imagine a simple online store. You could store everything in one table:
This has problems:
- Alice's email is stored twice — if she changes it, you must update multiple rows
- The Keyboard's price is stored twice — if it changes, you must update multiple rows
- Adding a new customer who has not ordered yet is impossible without a fake order
- Removing all orders for a customer accidentally removes their account
The solution: split the data into separate tables, each representing one concept, and connect them with foreign keys.
Now Alice's email lives in one place. The Keyboard's price lives in one place. Joins let you reconstruct the combined view when you need it.
Setting Up the Example Schema
Create these tables to follow along:
INNER JOIN — Only Matching Rows
An INNER JOIN returns rows that have a match in both tables. If a customer has no orders, they do not appear. If an order references a customer that doesn't exist, it doesn't appear.
Notice:
- David does not appear — he has no orders
- Laptop Sleeve does not appear — nobody ordered it
The syntax:
Using aliases to shorten table names:
LEFT JOIN — Keep All Left Rows
A LEFT JOIN returns all rows from the left table, plus matching rows from the right. If there is no match in the right table, the right side columns are NULL.
David appears with NULL values for the order columns because he has no orders — but he is not excluded. This is the key difference from INNER JOIN.
The most common use of LEFT JOIN: finding rows that have NO match.
RIGHT JOIN — Keep All Right Rows
RIGHT JOIN is the mirror of LEFT JOIN — it keeps all rows from the right table. It is used rarely in practice because you can always rewrite a RIGHT JOIN as a LEFT JOIN by swapping the table order.
Use LEFT JOIN consistently and avoid RIGHT JOIN — it makes queries easier to read.
FULL OUTER JOIN — Keep All Rows from Both Sides
FULL OUTER JOIN returns all rows from both tables. Where there is no match, the missing side has NULLs.
Used less frequently than INNER and LEFT joins, but useful for finding unmatched rows on either side.
CROSS JOIN — Every Combination
A CROSS JOIN produces the Cartesian product — every row in the left table combined with every row in the right table.
This is rarely intentional. If you write a JOIN without an ON clause, you accidentally get a cross join — a common beginner mistake.
Self-Joins — A Table Joining Itself
Sometimes you need to join a table to itself. The classic example: an employee table where each employee has a manager_id that references another employee's id.
The key: you use aliases (e and m) to distinguish the two "instances" of the same table.
Joining Three or More Tables
Joins chain naturally — each new JOIN adds another table:
Common Join Mistakes
Mistake 1: Duplicate rows from one-to-many joins
If a customer has 5 orders, joining customers to orders gives 5 rows for that customer. This is correct and expected — but surprises beginners who then aggregate incorrectly.
Mistake 2: Ambiguous column names
When two joined tables have a column with the same name, you must qualify which table's column you want:
Mistake 3: Missing the join condition
Practical Exercise: Complete Store Queries
Using the store database with customers, products, and orders:
Join Type Summary
| Join Type | Returns |
|---|---|
INNER JOIN | Only rows that match in both tables |
LEFT JOIN | All rows from left table + matches from right (NULLs where no match) |
RIGHT JOIN | All rows from right table + matches from left (use LEFT JOIN instead) |
FULL OUTER JOIN | All rows from both tables (NULLs where no match on either side) |
CROSS JOIN | Every combination of left × right rows |
When to use which:
- Finding related data →
INNER JOIN - Keeping all records even without a match →
LEFT JOIN - Finding records with no match →
LEFT JOIN ... WHERE right.id IS NULL - Comparing two complete sets →
FULL OUTER JOIN
Module F-6 covers constraints — the rules that make your database trustworthy: PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL, and CHECK.
Next: F-6 — Constraints and Data Integrity →
A software architect is designing a new microservice that interacts with a highly normalized relational database. They are considering denormalizing some data within the microservice's local cache to improve read performance. What is the primary architectural trade-off introduced by the *necessity* of using JOINs in a normalized schema, which this architect is implicitly trying to mitigate?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
Sign in & RegisterQuestion 2: A data analyst reports that a dashboard showing 'Total Revenue by Customer' is displaying NULL or 0 for some customers, even though the customers table clearly lists them. The SQL query used for the dashboard is:
As a Senior Principal Software Engineer, what is the most likely reason for this discrepancy and the correct immediate fix to include all customers, showing 0 for those with no orders?
- A) A. The
productstable has missing price data, causingNULLs in theSUM. The fix is to add aWHERE p.price IS NOT NULLclause. - B) B. The
JOINbetweencustomersandordersis anINNER JOIN, which excludes customers without matching orders. The fix is to change it to aLEFT JOINand useCOALESCEforSUM. - C) C. The
GROUP BYclause is incorrect and should includeo.idto ensure all orders are counted. The fix is to addo.idtoGROUP BY. - D) D. The
SUMfunction implicitly handlesNULLs as0, so the issue must be thatquantityorpriceare0. The fix is to check theordersandproductsdata.
Reveal Answer
Correct Answer: B
The lesson explicitly states that INNER JOIN (which JOIN defaults to) 'returns rows that have a match in both tables' and demonstrates that customers without orders (like David) 'do not appear' in the result. The problem describes customers being entirely missing or showing NULL/0 revenue, which is characteristic of INNER JOIN excluding them. The correct fix is to use a LEFT JOIN from customers to orders to ensure all customers are included. For customers with no orders, the orders and products columns will be NULL, causing SUM(o.quantity * p.price) to evaluate to NULL. To display 0 instead of NULL for these customers, COALESCE(SUM(o.quantity * p.price), 0) should be used. Option A is incorrect because INNER JOIN would already filter out rows where price is NULL if it's part of the join or calculation, but the primary issue is missing customers. Option C is incorrect; GROUP BY c.name is appropriate for summing per customer. Option D is incorrect; while SUM ignores NULLs, the core problem is the exclusion of entire customer rows, not just NULL values within existing rows.
Question 3: During a peak traffic event, a critical reporting service experiences severe database load, leading to timeouts and degraded user experience. Upon investigation, a newly deployed SQL query for a 'product compatibility matrix' report is identified as the culprit. The query looks like this:
Assuming the products table has N rows, what is the primary performance issue this query introduces, and what is the most direct consequence in a production environment?
- A) A. It creates an
N*NCartesian product, leading to excessive memory usage and network transfer for the result set. - B) B. It causes a deadlock because
p1andp2are aliases of the same table, leading to contention. - C) C. It results in an
Nrow output, but the database optimizer struggles with the implicit join syntax, causing slow execution. - D) D. It performs an
INNER JOINby default, but without anONclause, it scans the entire table multiple times, leading to high I/O.
Reveal Answer
Correct Answer: A
The lesson explicitly warns that 'If you write a JOIN without an ON clause, you accidentally get a cross join' and highlights SELECT * FROM customers, orders; as an 'old-style implicit join syntax' that is an 'ACCIDENTAL cross join.' A CROSS JOIN produces a Cartesian product, meaning every row from the first table is combined with every row from the second table. If the products table has N rows, joining it to itself this way will generate N * N rows. For even moderately sized tables (e.g., N=10,000), this results in 100 million rows, which can quickly overwhelm database memory, CPU, and network bandwidth, leading to the observed timeouts and degraded performance (A). Option B is incorrect; self-joins with aliases are a standard and safe practice and do not inherently cause deadlocks. Option C is incorrect; the output is N*N rows, not N. Option D is incorrect; while it is an implicit CROSS JOIN, the primary issue is the massive result set size, not just multiple table scans, and it's not an INNER JOIN in the sense of matching rows.
Discussion
0Join the discussion