Intermediate: combining and changing data · JOINs · topic 22 of 62 · 35 min
LEFT, RIGHT and FULL OUTER JOIN
Explanation
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.
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 ordersCounting 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.
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 0The 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.
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 10000mAhRIGHT 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.
-- 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.
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| Database | RIGHT JOIN | FULL OUTER JOIN |
|---|---|---|
| PostgreSQL, SQL Server, Oracle | Yes | Yes |
| SQLite | Yes, since 3.39 (2022) | Yes, since 3.39 |
| MySQL, MariaDB | Yes | No: emulate it with LEFT JOIN ... UNION ... RIGHT JOIN |
-- 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;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
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.
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;Practice (0/4 done)
Show every customer with the number of orders they placed, including customers with 0 orders. Return name and order_count.
Which products have never been ordered? Use a LEFT JOIN from products to order_items. Return the product id and name.
In COMPANY, which departments have no employees? Return one column: the department name.
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
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Joining three or more tablesJOINs · same module
- Self joins and cross joinsJOINs · same module
- Joins: combining tablesBackend Development · SQL in depth
- IN, EXISTS and NOT EXISTSSubqueries, CTEs and set operations
- Correlated subqueries and derived tablesSubqueries, CTEs and set operations
- SELECT and WHERE: finding rowsBackend Development · Databases and SQL basics
Questions people ask about this
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.
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.
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.
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
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
- 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.
- Module 5 exam: JOINs · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Subqueries, CTEs and set operations. 62 topics left in the course before the final exam.