Skip to content

Intermediate: combining and changing data · JOINs · topic 22 of 62 · 35 min

LEFT, RIGHT and FULL OUTER JOIN

Explanation

An outer join keeps rows even when they have no partner, and fills the missing side with NULL. LEFT JOIN keeps every row of the left table, RIGHT JOIN every row of the right, and FULL OUTER JOIN every row of both. LEFT JOIN plus WHERE right.id IS NULL finds rows with no match, such as customers who never ordered.

Why outer joins?

An inner join answers "show me the pairs". Many business questions are about everyone, including those with nothing: every customer with their number of orders (0 included), every product with its sales, every department with its headcount. If you use an inner join there, the zeros silently vanish, and the report looks complete when it is not.

LEFT JOIN

LEFT JOIN (also written LEFT OUTER JOIN) keeps every row of the table written before it, the left table. When a left row finds a partner, the columns of the right table are filled in as usual. When it finds none, the row is still kept, and every right-hand column is NULL.

sql
SELECT c.name, o.id AS order_id, o.status
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id;
-- Asha Verma  | 101  | Delivered
-- Asha Verma  | 103  | Delivered
-- ...
-- Sneha Patil | 107  | Placed
-- Arjun Reddy | NULL | NULL      <- kept, no orders
9 rows: 8 orders, plus Arjun Reddy with NULLs because he never ordered.

Counting with zeros

Combine a LEFT JOIN with GROUP BY from module 4. Count a column from the right table, not *. COUNT(o.id) skips NULLs, so Arjun gets 0. COUNT(*) counts rows, and his single NULL-filled row would wrongly give 1.

sql
SELECT c.name, COUNT(o.id) AS order_count
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name;
-- Asha Verma 3, Ravi Kumar 2, Priya Nair 1, Imran Khan 1, Sneha Patil 1, Arjun Reddy 0

The anti-join: rows with no match

An anti-join returns the rows of one table that have no partner in another. With a LEFT JOIN, the unmatched rows are exactly the ones where the right table's key is NULL. Test the right table's primary key (or the join column), because it is never NULL in a real match.

sql
SELECT p.id, p.name
FROM products AS p
LEFT JOIN order_items AS oi ON oi.product_id = p.id
WHERE oi.product_id IS NULL;
-- 8 | Power Bank 10000mAh
The product that has never been sold. Module 6 shows the same idea with NOT EXISTS.

RIGHT JOIN

RIGHT JOIN keeps every row of the table written after it. a RIGHT JOIN b is the same as b LEFT JOIN a, with the columns in a different order. Most teams write only LEFT JOINs, because reading "keep everything from the table I started with" is easier. If you meet a RIGHT JOIN, swap the two tables and make it a LEFT JOIN.

sql
-- These two return the same 9 rows:
SELECT c.name, o.id FROM orders o RIGHT JOIN customers c ON c.id = o.customer_id;
SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON o.customer_id = c.id;

FULL OUTER JOIN

FULL OUTER JOIN keeps unmatched rows from both sides. It is useful for comparing two lists, for example last month's and this month's stock, or employees and departments, where both sides may have orphans.

sql
SELECT e.name AS employee, d.name AS department
FROM employees AS e
FULL OUTER JOIN departments AS d ON d.id = e.dept_id;
-- ... 10 matched pairs ...
-- Deepak Yadav | NULL    <- employee with no department
-- NULL         | Legal   <- department with no employee
12 rows. Supported by PostgreSQL, SQL Server, Oracle and SQLite 3.39 or later.
DatabaseRIGHT JOINFULL OUTER JOIN
PostgreSQL, SQL Server, OracleYesYes
SQLiteYes, since 3.39 (2022)Yes, since 3.39
MySQL, MariaDBYesNo: emulate it with LEFT JOIN ... UNION ... RIGHT JOIN
sql
-- MySQL: emulate FULL OUTER JOIN
SELECT e.name, d.name FROM employees e LEFT JOIN departments d ON d.id = e.dept_id
UNION
SELECT e.name, d.name FROM employees e RIGHT JOIN departments d ON d.id = e.dept_id;
UNION (module 6) merges the two results and removes the repeated matched rows.

Common mistakes

  • Using COUNT(*) after a LEFT JOIN and getting 1 instead of 0. Count a right-table column, such as COUNT(o.id).
  • Testing the wrong column for the anti-join, for example WHERE o.status IS NULL, which also matches real orders with an unknown status. Test the right table's key.
  • Putting a filter on the right table in WHERE, which removes the NULL rows and turns the LEFT JOIN back into an inner join. Topic 5 shows the fix: put it in ON.
  • Writing FULL OUTER JOIN in MySQL and getting a syntax error. Use the UNION version.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

LEFT, RIGHT and FULL OUTER JOIN · 1/7

LEFT, RIGHT and FULL OUTER JOIN

Inner joins drop rows with no partner. Outer joins keep them and fill the gaps with NULL.

Prefer a video by a teacher? Find free videos on “sql left join right join full outer join anti join explained” on YouTube ↗

In real life: Finding inactive users for a win-back campaign

Food delivery and shopping apps regularly look for people who signed up but never ordered, and send them a first-order coupon. That list is a LEFT JOIN from users to orders with WHERE orders.id IS NULL. The same pattern finds products that never sold (to discount or delist them) and teachers with no class assigned in a school timetable.

sql
SELECT c.id, c.name, c.city
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
WHERE o.id IS NULL;
Returns 6 | Arjun Reddy | Hyderabad: he joined in March 2025 and has not ordered yet.

Practice (0/4 done)

Task 1 · Code · SQL

Show every customer with the number of orders they placed, including customers with 0 orders. Return name and order_count.

Task 2 · Code · SQL

Which products have never been ordered? Use a LEFT JOIN from products to order_items. Return the product id and name.

Task 3 · Code · SQL

In COMPANY, which departments have no employees? Return one column: the department name.

Task 4 · Code · SQL

Compare employees and departments with a FULL OUTER JOIN so that the employee with no department and the department with no employees both appear. Return two columns: employee (the employee name) and department (the department name).

Quick quiz

Question 1

1.customers LEFT JOIN orders: what appears for a customer with no orders?

Question 2

2.After customers LEFT JOIN orders ... GROUP BY customer, which gives 0 for a customer with no orders?

Question 3

3.orders RIGHT JOIN customers is the same as...

Question 4

4.You need a FULL OUTER JOIN in MySQL 8. What do you do?

Question 5

5.In the anti-join products LEFT JOIN order_items ... WHERE ? IS NULL, which column is the safest to test?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

customers LEFT JOIN orders: what appears for a customer with no orders?

Answer: One row with the customer's columns and NULL in every order column

LEFT JOIN keeps every left row and fills the missing side with NULL.

After customers LEFT JOIN orders ... GROUP BY customer, which gives 0 for a customer with no orders?

Answer: COUNT(o.id)

COUNT(column) skips NULLs; the unmatched row has o.id NULL.

orders RIGHT JOIN customers is the same as...

Answer: customers LEFT JOIN orders

Swap the tables and change RIGHT to LEFT.

You need a FULL OUTER JOIN in MySQL 8. What do you do?

Answer: Combine a LEFT JOIN and a RIGHT JOIN with UNION

MySQL has no FULL OUTER JOIN, so it is emulated with UNION.

In the anti-join products LEFT JOIN order_items ... WHERE ? IS NULL, which column is the safest to test?

Answer: oi.product_id, the join column of the right table

The right table's join key is NULL only when there was no match. p.id is never NULL, and qty could be NULL in a real row.

Customers has 7 rows; 2 of them have no orders. What does an INNER JOIN of customers with orders do with those 2 customers?Related

Answer: Leaves them out

INNER JOIN keeps only rows that have a match on both sides. A LEFT JOIN would keep them with NULLs.

From Joins: combining tables

customers LEFT JOIN orders ... WHERE o.status = 'Delivered'. What happens to customers with no Delivered orders?Related

Answer: They are removed, because NULL = 'Delivered' is not true

Move the condition into ON to keep them.

From Join pitfalls and how to avoid them

customers LEFT JOIN orders ... JOIN order_items (an inner join). What happens to Arjun, who has no orders?Related

Answer: He is dropped by the inner join, because his NULL order id matches no order line

A later inner join removes the unmatched rows the LEFT JOIN kept. Use LEFT JOIN for the later joins too.

From Joining three or more tables

Employees has 11 rows; one has dept_id NULL. How many rows does employees INNER JOIN departments ON d.id = e.dept_id return (every other employee has a valid department)?Related

Answer: 10

The NULL key matches nothing, so that employee is dropped.

From Why joins, and INNER JOIN

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “JOINs” before its exam: Joining three or more tables, Self joins and cross joins, Join pitfalls and how to avoid them.
  2. Module 5 exam: JOINs · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Subqueries, CTEs and set operations. 62 topics left in the course before the final exam.

← Previous: Why joins, and INNER JOIN