Skip to main content

LEFT JOIN: Keeping All Left-Side Rows

LEFT JOIN returns all rows from the left table, plus matching rows from the right table. When there's no match in the right table, the right-side columns are filled with NULL.

This contrasts with INNER JOIN, which drops any row that doesn't have a match on both sides.

Prior lesson

The sample dataset is defined in the Introduction to SQL lesson.

Syntax

SELECT columns
FROM left_table AS l -- left table
LEFT JOIN right_table AS r ON l.key = r.key; -- right table

Venn diagram view

Join typeKeeps rows from left with no matchKeeps rows from right with no match
INNER JOINNoNo
LEFT JOINYes (NULLs on right side)No

Examples

Example 1: All customers, including those with no orders

Frank Lee (customer 6) exists in customers but has no orders. A LEFT JOIN keeps Frank in the result and fills the orders columns with NULL.

SELECT
c.customer_id,
c.name,
o.order_id,
o.total_amount
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id;

Result (all orders per customer, NULLs where no order exists):

customer_idnameorder_idtotal_amount
1Alice Johnson1999.00
1Alice Johnson259.98
1Alice Johnson6349.00
2Bob Smith3349.00
2Bob Smith7999.00
3Carol White4796.00
3Carol White9999.00
4Dave Brown549.90
4Dave Brown1024.95
5Eve Davis889.97
6Frank LeeNULLNULL

Example 2: Find customers with NO orders

When the right-side columns are all NULL, the row had no match. Filtering on IS NULL isolates those rows — a classic anti-join pattern.

SELECT
c.customer_id,
c.name
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;

Result:

customer_idname
6Frank Lee

Common Mistake

Filtering on the right-side table in WHERE turns LEFT JOIN into INNER JOIN

Once you add a WHERE condition on the right-side table, rows that were NULL (unmatched) fail the condition and get dropped - effectively converting your LEFT JOIN into an INNER JOIN.

-- This looks like a LEFT JOIN but behaves like INNER JOIN:
SELECT c.name, o.total_amount
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id
WHERE o.status = 'completed'; -- drops NULLs!

-- To filter on the right side while preserving NULLs, put the condition in ON:
SELECT c.name, o.total_amount
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id
AND o.status = 'completed';

Practice

Write a query that returns each customer's name, city, order_id, and order_date. Include customers who have never placed an order.

Show answer
SELECT
c.name,
c.city,
o.order_id,
o.order_date
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id;

Expected result:

namecityorder_idorder_date
Alice JohnsonNew York12024-01-05
Alice JohnsonNew York22024-01-10
Alice JohnsonNew York62024-03-01
Bob SmithLondon32024-01-15
Bob SmithLondon72024-03-15
Carol WhiteSydney42024-02-01
Carol WhiteSydney92024-04-10
Dave BrownToronto52024-02-10
Dave BrownToronto102024-05-01
Eve DavisNew York82024-04-01
Frank LeeNULLNULLNULL

Frank Lee has no orders, so all order columns are NULL.

Donate to this project