LEFT JOIN: Keeping Rows That Don't Match
Use LEFT JOIN to list every movie alongside its audience rating, including movies that have never been rated.
Overview
A LEFT JOIN returns every row from the left (first) table, and the matching
row(s) from the right table when one exists — or NULL in every right-table
column when it doesn't. This is the tool you reach for whenever "no match"
is itself meaningful information, rather than something you want to discard.
A classic case: not every movie in a catalog has been rated by every source.
An INNER JOIN between movies and ratings would silently hide movies
with no rating at all. A LEFT JOIN keeps them in the result, with NULL
standing in for "we don't have that rating."
The Query
Loading database engine...
Step-by-Step Breakdown
FROM movies m— the left table. Every row from here is guaranteed to appear in the output at least once.LEFT JOIN ratings r ON m.id = r.movie_id AND r.source = 'Audience'— attach the matchingratingsrow, but only the one wheresource = 'Audience'. Notice the source filter lives inside theONclause, not in aWHEREclause — that distinction matters for outer joins (see Common Mistakes).SELECT m.title, m.genre, r.score AS audience_score— if no audience rating exists for a movie,r.score(and every otherratingscolumn) comes back asNULLfor that row, but the movie row itself is still included.ORDER BY m.title— alphabetical for readability.
Several movies in this dataset only have a "Critics" rating and no
"Audience" rating — those rows will show NULL for audience_score instead
of disappearing.
Variations
Find exactly which movies have no audience rating, using the "anti-join" pattern:
Loading database engine...
This is a common and useful idiom: do a LEFT JOIN, then filter with
WHERE <right_table>.<any_not_null_column> IS NULL to isolate rows that
didn't find a match. It effectively answers "which movies exist that
ratings doesn't have an audience score for?"
ON vs. WHERE changes the meaning
If you moved r.source = 'Audience' out of the ON clause and into a
WHERE clause instead, the query would stop behaving like a left join.
WHERE runs after the join, so it would filter out every row where
r.source isn't 'Audience' — including the NULL rows for movies with
no rating at all, silently turning your left join back into something
closer to an inner join.
Common Mistakes
- Putting join-specific filters in
WHEREinstead ofON. As shown above, this quietly discards the unmatched rows aLEFT JOINwas meant to preserve. - Checking the wrong column for
NULL. Use a column from the right table that can never beNULLwhen a match does exist — typically its primary key (r.id), not an arbitrary column that might legitimately beNULL. - Forgetting which table is "left."
A LEFT JOIN Bkeeps all ofA; swapping the table order changes the result. If you want all rows fromratingsinstead, either reorder the join or useRIGHT JOIN. - Expecting aggregate functions to ignore the extra NULL rows.
COUNT(*)over a left-joined result counts every row, including the ones withNULLin every right-table column — useCOUNT(r.id)if you want to count only actual matches.