JOINs: combining tables
INNER, LEFT and RIGHT joins, table aliases, multi-table joins and finding unmatched rows.
Real applications spread data across many tables: customers in one, orders in another, products in a third. JOINs combine them in a single query by matching rows on related columns, usually a foreign key matched to a primary key.
Sample data#
INNER JOIN: only matching rows#
The ON clause is the matching rule. Each customer row is paired with every order row whose customer_id equals its id. Grace is missing because she has no matching order. That is what inner means: only the intersection. Plain JOIN is the same as INNER JOIN.
Table aliases (c, o) keep queries short, and they're required when both tables have a column with the same name (id). Get into the habit of prefixing every column with its alias in joins.
LEFT JOIN: keep everything on the left#
Every customer appears. For Grace, the order columns are NULL. LEFT JOIN is the same as LEFT OUTER JOIN.
Finding rows without a match (an anti-join) is a classic LEFT JOIN trick:
ON vs WHERE in outer joins#
This is the most common outer-join bug. Suppose you want every customer together with their paid orders:
Rule of thumb: conditions on the optional (right-hand) table belong in ON, and conditions on the preserved (left-hand) table belong in WHERE.
RIGHT JOIN#
RIGHT JOIN is the mirror image: it keeps every row from the right-hand table. A RIGHT JOIN B gives the same rows as B LEFT JOIN A, and most people write LEFT JOINs because they read naturally from left to right.
Joining many tables#
Chain joins to follow relationships. Here is every order line with customer, product and line total:
order_items is a junction table: it turns the many-to-many relationship between orders and products into two one-to-many relationships.
Joins with aggregates#
Total spent per customer, including customers who've spent nothing:
Note COUNT(DISTINCT o.id). Order 101 has two items, so after joining items it appears on two rows, and plain COUNT(o.id) would count it twice. Joins multiply rows, so watch your counts and sums whenever you join a one-to-many relationship.
Choosing the right join#
Common mistakes#
- Forgetting the ON clause. In MySQL,
JOINwithoutONis a cross join, and every row pairs with every row. - Ambiguous columns.
SELECT id ...with two tables that both haveidfails with "Column 'id' in field list is ambiguous". Prefix with aliases. - Filtering the outer table in WHERE. That silently turns LEFT JOINs into inner joins.
- Double counting after one-to-many joins. Use
COUNT(DISTINCT ...), or aggregate in a subquery before joining. - Joining on columns without indexes. Foreign-key columns should be indexed (InnoDB does this automatically for declared foreign keys).
What's next#
Next you'll learn the remaining join shapes: joining a table to itself, CROSS JOIN, USING and how to get a FULL OUTER JOIN in MySQL.
Check your understanding
Quick quiz
1.What does an INNER JOIN return?
2.You want every customer, including those with no orders. Which join?
3.In a LEFT JOIN from customers to orders, where should the condition
o.status = 'paid'go to keep customers without paid orders in the result?
Finished reading?
Mark this lesson complete to track your progress.