What does LEFT OUTER JOIN return for left rows that have no match on the right?
answer
- one side of the join is preserved
- unmatched rows are not thrown away
- the missing columns still need values
- right-side columns come back NULL
basics
~10 sLEFT OUTER JOIN keeps every row of the left table. When the ON predicate matches no right row, the left row is still returned once, with every right-side column filled in as NULL.
solid answer
~50 s`LEFT OUTER JOIN` names a **preserved** side: the table written to the left of the keyword. Conceptually each left row is matched against the right table using the `ON` predicate. A left row with matches produces one output row per match, exactly like an inner join. A left row with no match is not discarded — it is *NULL-extended*: emitted once, with every column that comes from the right table set to `NULL`. So the result is the inner-join result plus the unmatched left rows. `OUTER` is an optional noise word, so `LEFT JOIN` means the same thing, and `RIGHT OUTER JOIN` is the mirror image, preserving the table written after the keyword. Two things trip people up: those right-side `NULL`s are manufactured by the join rather than stored in the table, and the result can still hold more rows than the left table when a left row matches several right rows.
code
sql · 8 lines-- customers: (1,'Ada'), (2,'Grace')
-- orders: (10, customer_id 1, total 40.00)
SELECT c.id, c.name, o.id AS order_id, o.total
FROM customers c
LEFT OUTER JOIN orders o ON o.customer_id = c.id
ORDER BY c.id;
-- 1 | Ada | 10 | 40.00
-- 2 | Grace | NULL | NULLgo deeper
Be ready to say which table is preserved and to state out loud that unmatched right-side columns come back as NULL. Expect to be handed a two-table example and asked for the exact rows.
Explain the result as the inner-join rows plus the NULL-extended unmatched left rows, and note that OUTER is optional and that multiple matches still multiply rows.
Demonstrate that the NULLs are manufactured by the join rather than stored, and that assuming one output row per left row is exactly how reports quietly overcount once the right table stops being unique per key.
Own the house convention: a codebase where joins run in one direction from a clear driving table is far cheaper to review and to reason about than one that mixes directions through long join chains.
## What "outer" adds to a join An inner join returns only the row combinations that satisfy the `ON` predicate; a row on either side with no partner simply vanishes from the result. An **outer** join adds a guarantee: one side is *preserved*, meaning every one of its rows reaches the output whether or not the predicate found a partner. `LEFT OUTER JOIN` preserves the table written to the left of the keyword. `RIGHT OUTER JOIN` preserves the one written to the right. The keyword `OUTER` carries no meaning of its own — `LEFT JOIN` and `LEFT OUTER JOIN` are the same operator, just as `JOIN` and `INNER JOIN` are. ## How the result is built A useful mental model, ignoring how any engine actually executes it: 1. Form every pairing of a left row with a right row. 2. Keep the pairings for which the `ON` predicate evaluates to true. (Predicates that evaluate to false *or unknown* are discarded — `ON` keeps only true.) 3. Then, for each left row that produced no surviving pairing, add one output row consisting of that left row plus a `NULL` in every column drawn from the right table. Step 3 is the whole of the outer join. Steps 1–2 alone are an inner join, so the identity worth memorising is: **left outer join = inner join result + the unmatched left rows, NULL-extended.** ```sql SELECT c.name, o.id AS order_id FROM customers c LEFT OUTER JOIN orders o ON o.customer_id = c.id; -- Ada, 10 -- Grace, NULL <- Grace has no orders, but survives ``` ## NULL-extension is manufactured, not stored The `NULL` in `order_id` above does not exist anywhere in `orders`. The join invented it to fill columns for which there is no row. That matters when reading a result: a `NULL` in a right-side column can mean either "this left row matched nothing" or "it matched a row whose value happens to be NULL". If you need to distinguish those, test a right-side column that is never `NULL` in the source table, typically its primary key or the join key itself. It also means the right-side columns of a preserved row become nullable *expressions* even if the underlying columns are declared `NOT NULL`. Any downstream arithmetic, concatenation or comparison on them inherits `NULL` semantics: `o.total * 2` is `NULL`, and `o.total > 0` is unknown, not false. ## What LEFT JOIN does not promise It promises *at least* one output row per left row, not *exactly* one. If a left row matches three right rows, it appears three times. Candidates who assume "the result has as many rows as the left table" get burned the moment the right table is not unique per join key. It also does not promise anything about the final result of the whole statement. `LEFT JOIN` describes what that one join operator produces; clauses evaluated later can still remove those preserved rows. ## The RIGHT mirror `a RIGHT OUTER JOIN b ON …` preserves `b`. It is exactly the same operator with the sides swapped, which is why `FROM a RIGHT JOIN b` and `FROM b LEFT JOIN a` return the same rows. Most teams write only `LEFT JOIN` so the preserved, driving table is always the one you have already read. ## Presenting the NULLs When a report should show a placeholder rather than an empty cell, wrap the right-side expression: ```sql SELECT c.name, COALESCE(o.status, 'no orders') AS status FROM customers c LEFT JOIN orders o ON o.customer_id = c.id; ``` `COALESCE` returns its first non-NULL argument, so unmatched customers read `no orders`. Note this changes the *display*, not the join: the row was already preserved. ## Why interviewers ask it Because almost every reporting requirement contains the phrase "including those with none" — every customer including those without orders, every department including empty ones, every day including days with no events. Recognising that sentence as "the left side is preserved" is the single most useful reflex in day-to-day SQL writing, and it is the setup for the classic follow-up: find the rows that matched nothing at all.
- Does writing LEFT JOIN instead of LEFT OUTER JOIN change anything?No. `OUTER` is an optional noise word in the standard grammar, so `LEFT JOIN` and `LEFT OUTER JOIN` are the identical operator. The same is true of `INNER` in `INNER JOIN`, and of `OUTER` in `RIGHT OUTER JOIN` and `FULL OUTER JOIN`. Teams pick one spelling for consistency; nothing about the result depends on it.
- The right-side column is declared NOT NULL. Can it still be NULL in the output?Yes. `NOT NULL` constrains what may be *stored* in the table. NULL-extension happens in the join's output, where there is no row to constrain, so the projected column is nullable regardless of the declaration. Any expression built on it — arithmetic, concatenation, comparison — inherits NULL semantics for those preserved rows.
- Can a LEFT JOIN return more rows than the left table has?Yes. Preservation guarantees *at least* one output row per left row, not exactly one. A left row matching three right rows appears three times; a left row matching none appears once, NULL-extended. So the output row count is at least the left row count and can be far larger when the right table is not unique per join key.
Think of the left table as the guest list and the right table as the sign-in sheet. An inner join prints only guests who signed in; a left outer join prints every guest, leaving the sign-in columns blank for those who never showed up.
saying these in an interview costs you the question
- Says LEFT JOIN returns only rows present in both tables
- Thinks the NULLs are values stored in the right table
- Assumes the output always has exactly as many rows as the left table
- Believes LEFT JOIN and LEFT OUTER JOIN behave differently
- Claims the preserved side is decided by the ON clause's column order