INNER JOIN vs LEFT JOIN in SQL
INNER JOIN keeps matches only. LEFT JOIN keeps every row from the left table.
An INNER JOIN returns only rows with a match on both sides. That is often what you want, but it hides rows that have no related record.
A LEFT JOIN keeps every row from the left table. Missing matches from the right table appear as NULL.
INNER JOIN vs LEFT JOIN at a glance
| Join type | Rows it keeps | Use it when |
|---|---|---|
INNER JOIN | Only rows that match in both tables. | You only want records with a related row. |
LEFT JOIN | Every row from the left table, plus matching rows from the right table. Missing right-side values become NULL. | You need to keep unmatched left rows, such as customers with no orders. |
select left_table.id, count(right_table.id) as match_count
from left_table
left join right_table
on left_table.id = right_table.left_id
group by left_table.idOpen the DuckDB playground with the matching dataset and query already filled in.
A concrete example
In the Garden Shop data, some customers have orders and some do not. AnINNER JOIN between customers andorders drops customers with no orders because there is no matching order row to return.
select c.customer_id,
c.first_name,
o.order_id
from customers c
inner join orders o
on o.customer_id = c.customer_id
order by c.customer_id, o.order_idA LEFT JOIN starts from customers and keeps every customer. If a customer has no matching order, the order columns areNULL.
select c.customer_id,
c.first_name,
o.order_id
from customers c
left join orders o
on o.customer_id = c.customer_id
order by c.customer_id, o.order_idChoose based on missing matches
Start with the question you are answering. If you only care about customers who placed orders, an inner join is fine. If you need every customer, including customers with no orders, put customers on the left and use a LEFT JOIN.
Count carefully after a left join. count(*) counts the kept left row even when the right side is missing; count(o.order_id) counts only matched orders, which is why no-order customers show 0.
Common JOIN gotchas
- A
LEFT JOINcan accidentally behave like anINNER JOINif you put a right-table filter inWHERE. Seewhy a LEFT JOIN turns into an INNER JOIN. - For
INNER JOIN, keep relationship logic inONand row filters inWHERE. Seewhere INNER JOIN filters belong. - If a join makes counts or sums too high, check whether the join multiplied rows. Seewhy COUNT or SUM is too high after a JOIN.
Schema · Garden ShopTable · customers9 columns · 20 rows
One row per customer. Some customers have no phone on file.
Return every customer with their totalorder count. Include customers who have placed no orders, sorted so customers with no orders appear first.
- Columns: customer_id, first_name, last_name, order_count.
- Rows: 20 customers.
- Four customers should have order_count 0: Liam, Emma, Chloe, and Daniel.