Contents

Backend Development › Relational Databases & SQL

INNER, LEFT, RIGHT and FULL JOIN

Which rows each kind of join keeps.

Also known as: INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, outer join, LEFT OUTER JOIN

The join type decides which rows survive when the two tables don’t match perfectly.

Take two small tables:

customers            orders
id | name            id | customer_id | total
1  | Ana             10 | 1           | 50
2  | Bo              11 | 1           | 20
3  | Cy              12 | 9           | 70     <- customer 9 doesn't exist
JoinKeepsResult
INNER JOINOnly rows with a match in bothAna (2 rows)
LEFT JOINAll left rows, plus matches (NULLs where none)Ana (2 rows), Bo (NULL), Cy (NULL)
RIGHT JOINAll right rows, plus matchesAna (2 rows), and order 12 with NULL name
FULL JOINEverything from both sidesAll of the above
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

INNER is the default for a bare JOIN. LEFT JOIN is the one you’ll use most: “all customers, with their orders if they have any.”

Finding what’s missing

A left join plus a NULL check finds rows with no match:

SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;           -- customers who never ordered

The classic mistake

Filtering the right-hand table in WHERE turns a left join into an inner join:

-- Loses customers with no paid orders (their o.status is NULL, so the condition fails)
SELECT c.name, o.total
FROM customers c LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'paid';

-- Keeps them: put the condition in the ON clause
SELECT c.name, o.total
FROM customers c LEFT JOIN orders o ON o.customer_id = c.id AND o.status = 'paid';

Other notes: a RIGHT JOIN is just a LEFT JOIN with the tables swapped, and most people write only left joins. FULL JOIN is useful for comparing two datasets (what’s in A but not B, and vice versa) and isn’t supported by every database. A cross join has no matching condition and pairs every row with every row.