LEFT JOIN
How to combine tables while keeping all rows from the left table
What is a LEFT JOIN?
A LEFT JOIN combines rows from two tables based on a matching column. It returns every row from the left table, and the matching rows from the right table. When no match exists in the right table, the columns from the right table are filled with NULL.
SELECT customers.first_name, customers.last_name, orders.order_id FROM customers LEFT JOIN orders ON customers.customer_id = orders.customer_id;
Every customer appears in the results — even those who have never placed an order. Those customers will show NULL for order_id.
LEFT JOIN vs INNER JOIN
The key difference is how unmatched rows are handled:
INNER JOIN — only returns rows where a match exists in both tables. Customers with no orders would not appear at all.
LEFT JOIN — returns all rows from the left table regardless of whether a match exists. Customers with no orders appear with NULL values for the order columns.
-- INNER JOIN: only customers who have placed at least one order SELECT customers.first_name, orders.order_id FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id; -- LEFT JOIN: all customers, NULL for those with no orders SELECT customers.first_name, orders.order_id FROM customers LEFT JOIN orders ON customers.customer_id = orders.customer_id;
💡 Which table is "left"?
The left table is whichever table appears after FROM. The right table is the one after LEFT JOIN. All rows from the left table are always preserved.
| Join type | Rows returned |
|---|---|
INNER JOIN | Only rows with a match in both tables |
LEFT JOIN | All rows from the left table; NULLs for unmatched right rows |
RIGHT JOIN | All rows from the right table; NULLs for unmatched left rows |
FULL JOIN | All rows from both tables; NULLs where no match exists |
Finding rows with no match
One of the most useful patterns with LEFT JOIN is finding rows in the left table that have no corresponding row in the right table. You do this by filtering for NULL in a right-table column after the join.
-- Find all customers who have never placed an order SELECT customers.first_name, customers.last_name FROM customers LEFT JOIN orders ON customers.customer_id = orders.customer_id WHERE orders.order_id IS NULL;
Because unmatched rows produce NULL in the right table columns, filtering for IS NULL isolates exactly those unmatched rows.
LEFT JOIN with GROUP BY
You can combine LEFT JOIN with GROUP BY to aggregate across the joined tables. A common use case is counting how many related records each row in the left table has — including a count of zero for rows with no matches.
-- Count orders per customer, including customers with zero orders
SELECT customers.first_name, customers.last_name,
COUNT(orders.order_id) AS order_count
FROM customers
LEFT JOIN orders ON customers.customer_id = orders.customer_id
GROUP BY customers.customer_id, customers.first_name, customers.last_name;
Notice COUNT(orders.order_id) rather than COUNT(*) — this counts only non-NULL values, so customers with no orders correctly get a count of 0.
Common mistakes
Filtering on a right-table column in WHERE instead of ON — writing WHERE orders.status = 'shipped' after a LEFT JOIN turns it into an effective INNER JOIN, because rows with NULL for orders.status won't satisfy the condition. If you need to filter on the right table while keeping unmatched left rows, put the condition in the ON clause instead.
Using COUNT(*) instead of COUNT(right_column) — COUNT(*) counts every row including those with all-NULL right-table columns, so unmatched left rows would be counted as 1 instead of 0. Use COUNT(right_table.column) to get accurate zero counts.
Confusing which table is left and which is right — the table after FROM is always the left table. Swap the table order if you need to preserve rows from the other table.
Practice: LEFT JOIN
Apply what you learned above. Write a query that matches each task.
Table Schema
Task
Tip: Ctrl + Enter to submit