Explain INNER, LEFT, RIGHT, and FULL OUTER JOIN with examples

5 minbeginnerjoinsinner-joinouter-join

Quick Answer

INNER JOIN returns only rows with a match in both tables. LEFT JOIN returns every row from the left table plus matching right-table columns (or NULL if no match). RIGHT JOIN is the mirror image. FULL OUTER JOIN returns all rows from both tables, filling in NULL wherever a match is missing on either side.

Detailed Answer

Given two tables:

-- customers: id, name
-- (1, 'Alice'), (2, 'Bob'), (3, 'Carol')

-- orders: id, customer_id, total
-- (101, 1, 50), (102, 1, 75), (103, 2, 20)
-- Note: Carol (id 3) has no orders, and there's no order for a nonexistent customer_id 9.

INNER JOIN — only matching rows

SELECT c.name, o.total
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id;

Result: Alice/50, Alice/75, Bob/20. Carol is excluded entirely because she has no matching order row.

LEFT JOIN — all of the left table, matches or NULL

SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

Result: Alice/50, Alice/75, Bob/20, Carol/NULL. Every customer appears at least once, even with no orders.

RIGHT JOIN — all of the right table, matches or NULL

SELECT c.name, o.total
FROM customers c
RIGHT JOIN orders o ON o.customer_id = c.id;

Functionally identical to swapping the tables and using LEFT JOIN. Most style guides prefer always writing LEFT JOIN and reordering the FROM/JOIN tables instead, since it reads more consistently across a codebase.

FULL OUTER JOIN — everything from both sides

SELECT c.name, o.total
FROM customers c
FULL OUTER JOIN orders o ON o.customer_id = c.id;

Result: every customer (even unmatched, like Carol) and every order (even one belonging to a since-deleted customer, if such a row existed). NULL fills in whichever side lacks a match. MySQL has no native FULL OUTER JOIN; it's emulated with LEFT JOIN UNION RIGHT JOIN.

Picking the right one

  • Need only rows that exist in both? Use INNER JOIN.
  • Need every row from a "primary" table regardless of whether related data exists? Use LEFT JOIN — e.g., all customers, with order totals if any.
  • Need a full reconciliation between two sets, including orphans on either side? Use FULL OUTER JOIN — common in data-quality/migration checks.