Skip to main content

LEFT JOIN

LEFT JOIN keeps every row from the left table.

When there is no match on the right side, right-table columns are NULL.

Core Concepts

flowchart LR
A[Left rows] --> B{Right match exists?}
B -->|Yes| C[Return combined row]
B -->|No| D[Return left row + NULL right columns]
BehaviorResult
Matched rowLeft + right values
Unmatched left rowLeft values + NULL on right

Code Examples

-- Add a customer with no orders.
INSERT INTO customers_join (customer_id, name)
VALUES (3, 'Grace');
Expected output
One new customer inserted (Grace).
-- Keep all customers, including those with no orders.
SELECT
c.customer_id,
c.name,
o.order_id,
o.total
FROM customers_join AS c
LEFT JOIN orders_join AS o
ON c.customer_id = o.customer_id
ORDER BY c.customer_id, o.order_id;
customer_idnameorder_idtotal
1Ada10149.99
1Ada10219.50
2Alan10375.00
3Gracenullnull
-- Find customers without orders.
SELECT c.customer_id, c.name
FROM customers_join AS c
LEFT JOIN orders_join AS o
ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL
ORDER BY c.customer_id;
customer_idname
3Grace

SQLite-Specific Nuances

SQLite Nuance

SQLite supports LEFT JOIN and the equivalent LEFT OUTER JOIN syntax.

Common Pitfalls / Best Practices

Pitfall

Adding WHERE filters on right-table columns without null-awareness, unintentionally removing unmatched left rows.

Best Practice

When looking for "missing matches," use LEFT JOIN plus WHERE right_key IS NULL.

Quick Challenge

List all customers with a derived column has_order (1 if matched, 0 if not).

View Solution
SELECT
c.customer_id,
c.name,
CASE WHEN o.order_id IS NULL THEN 0 ELSE 1 END AS has_order
FROM customers_join AS c
LEFT JOIN orders_join AS o
ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY c.customer_id;
customer_idnamehas_order
1Ada1
2Alan1
3Grace0