SQL · easy

Explain the difference between INNER, LEFT, and FULL OUTER JOIN.

Asked in Data Analyst interviews, in the SQL round.

Short answer

INNER keeps only matches, LEFT keeps all left rows plus matched right rows, FULL keeps all rows from both sides.

INNERLEFTFULL OUTER
Matched rowsKeptKeptKept
Left rows with no matchDroppedKept, right side NULLKept, right side NULL
Right rows with no matchDroppedDroppedKept, left side NULL
Row count vs left tableSame or fewerExactly the left countSame or more
Reach for it whenYou only want pairsYou are enriching a listYou are reconciling two sets
Which unmatched rows survive is the whole difference.

How to answer it

Definition

All three combine two tables on a condition. They differ in one thing only: which unmatched rows survive. The table above lays that out side by side.

Example

-- every customer, with their orders where any exist
SELECT c.customer_id, c.name, o.order_id, o.amount
FROM   customers c
LEFT   JOIN orders o ON o.customer_id = c.customer_id;

Output

customer_id | name  | order_id | amount
------------+-------+----------+--------
        101 | Ada   |     9001 | 120.00
        101 | Ada   |     9002 |  80.00
        204 | Ben   |     NULL |   NULL     <- no orders, kept anyway

Swap LEFT for INNER and Ben disappears. That single row is the entire difference, and it is usually the row the question is really about: customers who never ordered, events with no matching session, rows that failed to load.

The filter trap

A WHERE clause on the right-hand table turns a LEFT JOIN back into an INNER one, because a comparison against NULL is not true:

-- silently an INNER JOIN
SELECT c.name, o.amount
FROM   customers c
LEFT   JOIN orders o ON o.customer_id = c.customer_id
WHERE  o.amount > 100;

-- what was meant: the condition belongs in the join
LEFT   JOIN orders o ON o.customer_id = c.customer_id AND o.amount > 100;

Key points

FAQ

The 30-second answer

They all match rows on a condition, and the only difference is what happens to rows that do not match. INNER drops them from both sides. LEFT keeps every row of the left table and fills the right with NULLs. FULL OUTER keeps unmatched rows from both.
In practice I choose based on which unmatched rows I care about: LEFT when I am enriching a list and must not lose anyone, INNER when I only want pairs, FULL OUTER when reconciling two sources. The mistake I watch for is putting a condition on the right-hand table in the WHERE clause after a LEFT JOIN, because NULL fails the comparison and it quietly becomes an INNER join. That condition belongs in the ON clause.

In the interview

Practice this for real