SQLab Hub
Lesson 15 · RIGHT JOIN
Lesson 15 · Querying

SQL RIGHT JOIN: Syntax, Examples & Practice

SQL RIGHT JOIN returns every row from the right table, plus the matching rows from the left table. Where a right row has no match, it still appears, with NULL in the left table's columns. It is a LEFT JOIN with the two tables swapped, and RIGHT OUTER JOIN is the same thing.

⟖ Every right row kept ▶ Runnable Playground examples ⇄ RIGHT vs LEFT JOIN 🧪 7 practice exercises 📝 10-question quiz 🎤 12 interview questions

What Is a RIGHT JOIN in SQL?

The Playground database has an orders table and a users table of customers. Some customers have never ordered. This query starts from orders but still returns every customer, because users is on the right-hand side of a RIGHT JOIN:

SQLite · runs in the Playground
SELECT o.id AS order_id, o.total,
       u.id AS user_id, u.first_name
FROM orders o
RIGHT JOIN users u
  ON o.user_id = u.id
WHERE u.id <= 5
ORDER BY u.id, o.id;

▶ Run this query in the SQL Playground

Orders joined to the first five customers, keeping every customer
order_idtotaluser_idfirst_name
1094064.751Jennifer
131618.292Donna
NULLNULL3Kenneth
111518.364Michael
872501.234Michael
181843.74Michael
1274924.235Robert
✓ 7 rows · real output from the Playground database

Kenneth has no orders, but he is still in the result: the two columns that come from orders, the left table, are NULL. Michael has three orders and appears three times. Five customers produce 7 rows.

"Right" only means "written after the words RIGHT JOIN". The right table is the one whose rows are all kept; the left table supplies matches where they exist. An INNER JOIN would have dropped Kenneth.

RIGHT JOIN Syntax

SQL · syntax
SELECT columns
FROM left_table
RIGHT JOIN right_table
  ON left_table.key = right_table.key;
PartWhat it does
FROM left_tableSupplies matching rows only. Its columns are NULL for unmatched right rows.
RIGHT JOIN right_tableThe table whose rows are all kept.
ON …The matching rule, usually a foreign key compared with the primary key it points to.
RIGHT OUTER JOINThe full name of the same join. OUTER is optional.

Database support

DatabaseRIGHT JOIN
MySQL, MariaDBSupported
PostgreSQLSupported
SQL ServerSupported
OracleSupported
SQLiteSupported from version 3.39 (2022). In older versions, swap the tables and use LEFT JOIN.
The examples here run as written in the SQLabHub Playground, which uses SQLite 3.49, and work unchanged in the other databases.

How RIGHT JOIN Works

For each row of the right table, the database looks for rows in the left table that satisfy ON:

Right row has…Rows in the resultLeft-table columns
One match1Filled from the matching row
Several matchesOne per matchFilled from each matching row
No match1All NULL

Left rows that match nothing are dropped. The result therefore never has fewer rows than the right table:

SQLite · runs in the Playground
SELECT COUNT(*)             AS result_rows,
       COUNT(DISTINCT u.id) AS customers,
       COUNT(o.id)          AS matched_orders,
       COUNT(*) - COUNT(o.id) AS customers_without_orders
FROM orders o
RIGHT JOIN users u
  ON o.user_id = u.id;

▶ Run this query in the SQL Playground

Row counts of orders RIGHT JOIN users
result_rowscustomersmatched_orderscustomers_without_orders
23615020036
✓ 1 row · real output from the Playground database

All 150 customers are present: 200 rows for orders that matched, plus 36 NULL rows for customers who never ordered, 236 in total.

RIGHT JOIN vs LEFT JOIN

The same join, read from the other side.

A RIGHT JOIN B and B LEFT JOIN A are the same query. Both keep every row of B. Swap the two tables, change the keyword, and nothing else:

SQL
-- keeps every customer
SELECT u.first_name, o.id
FROM orders o
RIGHT JOIN users u
  ON o.user_id = u.id;

-- identical result
SELECT u.first_name, o.id
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id;
SQLite · runs in the Playground
SELECT 'orders RIGHT JOIN users' AS query,
       COUNT(*) AS rows_returned, COUNT(o.id) AS matched
FROM orders o
RIGHT JOIN users u ON o.user_id = u.id
UNION ALL
SELECT 'users LEFT JOIN orders', COUNT(*), COUNT(o.id)
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;

▶ Run this query in the SQL Playground

A RIGHT JOIN and the equivalent LEFT JOIN return the same rows
queryrows_returnedmatched
orders RIGHT JOIN users236200
users LEFT JOIN orders236200
✓ 2 rows · real output from the Playground database
LEFT JOINRIGHT JOIN
Keeps every row ofThe first (left) tableThe second (right) table
NULLs appear inRight-table columnsLeft-table columns
Unmatched rows dropped fromThe right tableThe left table
Rewrite as the otherSwap the tablesSwap the tables
How often it is writtenVery oftenRarely

Which one should you use?

Most teams write LEFT JOIN everywhere. A reader then knows the first table in FROM is the complete one, and every later join only adds optional detail. A query that mixes LEFT and RIGHT joins is much harder to follow. RIGHT JOIN is still worth knowing for three reasons:

Where you meet RIGHT JOINWhy
Existing and generated SQLReport builders, ORMs and older code produce it, and you have to read it correctly.
Adding a "must keep" table to a finished queryAppending RIGHT JOIN calendar can be a smaller change than rewriting the FROM clause.
InterviewsRewriting a RIGHT JOIN as a LEFT JOIN is a quick test of whether you understand join direction.

RIGHT JOIN vs INNER JOIN and FULL OUTER JOIN

To see all four joins give different answers, both tables need rows the other lacks. Take the customers who ordered in 2023 as the left side and those who ordered in 2024 as the right side:

SQLite · runs in the Playground
WITH y2023 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
), y2024 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
)
SELECT 'INNER JOIN' AS join_type, COUNT(*) AS customers
FROM y2023 a INNER JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'LEFT JOIN', COUNT(*)
FROM y2023 a LEFT JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'RIGHT JOIN', COUNT(*)
FROM y2023 a RIGHT JOIN y2024 b ON a.user_id = b.user_id
UNION ALL
SELECT 'FULL OUTER JOIN', COUNT(*)
FROM y2023 a FULL OUTER JOIN y2024 b ON a.user_id = b.user_id;

▶ Run this query in the SQL Playground

Customers returned by each join type between 2023 buyers and 2024 buyers
join_typecustomers
INNER JOIN19
LEFT JOIN51
RIGHT JOIN56
FULL OUTER JOIN88
✓ 4 rows · real output from the Playground database
JoinRowsMeaning
INNER JOIN19Ordered in both years
LEFT JOIN51Everyone who ordered in 2023
RIGHT JOIN56Everyone who ordered in 2024
FULL OUTER JOIN88Everyone who ordered in either year

The RIGHT JOIN rows show who is new in 2024: the left column is NULL for customers with no 2023 order.

SQLite · runs in the Playground
WITH y2023 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
), y2024 AS (
  SELECT DISTINCT user_id FROM orders
  WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
)
SELECT a.user_id AS ordered_2023,
       b.user_id AS ordered_2024
FROM y2023 a
RIGHT JOIN y2024 b
  ON a.user_id = b.user_id
ORDER BY b.user_id
LIMIT 6;

▶ Run this query in the SQL Playground

Customers who ordered in 2024, with their 2023 match if any
ordered_2023ordered_2024
NULL4
NULL5
88
1010
NULL11
1616
✓ 6 rows · real output from the Playground database

A FULL OUTER JOIN keeps the unmatched rows of both tables; RIGHT JOIN keeps them for the right table only. The WITH blocks at the top are CTEs, named subqueries that keep the joins readable.

Finding Unmatched Rows With RIGHT JOIN … IS NULL

After a RIGHT JOIN, a right row with no match has NULL in every left-table column. Filter on that with IS NULL to list the right rows that have nothing attached. Which products have never been reviewed?

SQLite · runs in the Playground
SELECT p.id, p.name, p.category
FROM reviews r
RIGHT JOIN products p
  ON r.product_id = p.id
WHERE r.id IS NULL
ORDER BY p.id;

▶ Run this query in the SQL Playground

Products with no reviews
idnamecategory
7Giant Stuffed Teddy BearToys
9Merino Wool SweaterClothing
13Retinol Eye Cream 30mlHealth & Beauty
14Play Kitchen DeluxeToys
19Resistance Bands SetSports & Outdoors
22Back Seat OrganizerAutomotive
✓ 27 rows · first 6 shown · real output from the Playground database

27 products have no review. Test the left table's primary key (r.id): it can only be NULL when no row matched.

Checking for orphaned rows

The same pattern is a data-quality check. An order should always belong to a customer; a RIGHT JOIN from users to orders keeps every order and exposes any that point at a customer who does not exist:

SQLite · runs in the Playground
SELECT COUNT(*) AS orders_without_a_customer
FROM users u
RIGHT JOIN orders o
  ON u.id = o.user_id
WHERE u.id IS NULL;

▶ Run this query in the SQL Playground

Orders whose customer does not exist
orders_without_a_customer
0
✓ 1 row · real output from the Playground database

The Playground data is clean, so the count is 0. On imported or migrated data this query is how broken references are usually found.

RIGHT JOIN With a WHERE Clause

A condition on the left table belongs in ON, or the join stops keeping every right row.

You want every order with its number of successful payments. The condition p.status = 'success' is about payments, the left table. Compare the two places it can go:

SQLite · runs in the Playground
SELECT 'condition in WHERE' AS version, COUNT(DISTINCT o.id) AS orders_returned
FROM payments p
RIGHT JOIN orders o ON p.order_id = o.id
WHERE p.status = 'success'
UNION ALL
SELECT 'condition in ON', COUNT(DISTINCT o.id)
FROM payments p
RIGHT JOIN orders o ON p.order_id = o.id AND p.status = 'success';

▶ Run this query in the SQL Playground

Orders returned when the filter is in WHERE and when it is in ON
versionorders_returned
condition in WHERE99
condition in ON200
✓ 2 rows · real output from the Playground database

In WHERE, only 99 of 200 orders remain. WHERE runs after the join, and for an order with no payment p.status is NULL, so the row fails the test. In ON, the condition only decides which payments count as a match:

SQLite · runs in the Playground
SELECT o.id AS order_id, o.status,
       COUNT(p.id) AS successful_payments
FROM payments p
RIGHT JOIN orders o
  ON p.order_id = o.id
 AND p.status = 'success'
WHERE o.id <= 6
GROUP BY o.id, o.status
ORDER BY o.id;

▶ Run this query in the SQL Playground

Successful payments per order, including orders with none
order_idstatussuccessful_payments
1cancelled0
2delivered2
3delivered2
4processing0
5delivered0
6shipped0
✓ 6 rows · real output from the Playground database
Condition on…Put it inBecause
The left tableONIt limits what can match and leaves unmatched right rows in place.
The right tableWHEREIt removes right rows you do not want at all (as o.id <= 6 does above).
"No match exists"WHERE left.id IS NULLThe one left-table test that belongs in WHERE.

RIGHT JOIN With GROUP BY and COUNT

A RIGHT JOIN keeps groups that would otherwise have no rows, so zeros show up in a report. How many directors does each department have?

SQLite · runs in the Playground
SELECT d.name AS department,
       COUNT(e.id) AS directors
FROM employees e
RIGHT JOIN departments d
  ON e.department_id = d.id
 AND e.title LIKE '%Director%'
GROUP BY d.id, d.name
ORDER BY directors DESC, d.name;

▶ Run this query in the SQL Playground

Directors per department, including departments with none
departmentdirectors
Product4
Customer Success2
Design2
HR2
Operations2
Engineering0
Finance0
Legal0
Marketing0
Sales0
✓ 10 rows · real output from the Playground database

Five departments have no director and still appear, with 0. Two things make that work: the title condition is in ON, and the count is COUNT(e.id), which ignores NULL. COUNT(*) would report 1 for those departments, because each still has one row. Other aggregates over no rows, such as SUM and AVG, return NULL; wrap them in COALESCE if you want 0.

RIGHT JOIN on Multiple Tables

Joins are applied in the order they are written. Each RIGHT JOIN keeps every row of the table it introduces, so in a chain the last table is the one that is complete:

SQLite · runs in the Playground
SELECT u.id AS user_id, u.first_name,
       o.id AS order_id, oi.product_id, oi.quantity
FROM order_items oi
RIGHT JOIN orders o
  ON oi.order_id = o.id
RIGHT JOIN users u
  ON o.user_id = u.id
WHERE u.id <= 3
ORDER BY u.id, o.id, oi.id;

▶ Run this query in the SQL Playground

Order items and orders joined to customers, keeping every customer
user_idfirst_nameorder_idproduct_idquantity
1Jennifer109434
2Donna13874
2Donna13301
2Donna13105
3KennethNULLNULLNULL
✓ 5 rows · real output from the Playground database

Kenneth has no order, so both the order and the item columns are NULL, but he is kept. The same report written with LEFT JOINs starts from the table that must be complete, which is why most people find it easier to read:

SQL · the LEFT JOIN version
SELECT u.id AS user_id, u.first_name,
       o.id AS order_id, oi.product_id, oi.quantity
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
LEFT JOIN order_items oi
  ON oi.order_id = o.id
WHERE u.id <= 3
ORDER BY u.id, o.id, oi.id;

Where RIGHT JOIN Is Used

QuestionLeft table → right table (kept)
Every department with its headcount, empty ones includedemployees → departments
Products that have never been ordered or reviewedorder_items / reviews → products
Orders that point at a missing customer (data audit)customers → orders
Sales rows whose product is not in the catalogueproducts → sales
Courses with no enrolled studentsenrollments → courses
Rooms with no booking tonightbookings → rooms
Every day on a chart, including days with no salessales → calendar
Pricing plans nobody has subscribed tosubscriptions → plans

Every row of this table could be written as a LEFT JOIN with the tables in the other order. Spreadsheet and BI tools offer the same join as "Right Outer" in their merge dialogs.

Common RIGHT JOIN Mistakes

1. Keeping the wrong table

❌ Keeps every order
SQL
SELECT u.first_name, o.id
FROM users u
RIGHT JOIN orders o
  ON o.user_id = u.id;
✅ Keeps every customer
SQL
SELECT u.first_name, o.id
FROM orders o
RIGHT JOIN users u
  ON o.user_id = u.id;

RIGHT JOIN keeps the table written after it. To keep every customer, users must be on the right, or use users LEFT JOIN orders.

2. Filtering the left table in WHERE

❌ Orders with no successful payment vanish
SQL
SELECT o.id, p.id
FROM payments p
RIGHT JOIN orders o
  ON p.order_id = o.id
WHERE p.status = 'success';
✅ Condition moved into ON
SQL
SELECT o.id, p.id
FROM payments p
RIGHT JOIN orders o
  ON p.order_id = o.id
 AND p.status = 'success';

For unmatched right rows the left columns are NULL, so a WHERE test on them fails and the join behaves like an INNER JOIN. See RIGHT JOIN with a WHERE clause.

3. Testing for NULL with =

❌ Returns no rows
SQL
SELECT p.name
FROM reviews r
RIGHT JOIN products p
  ON r.product_id = p.id
WHERE r.id = NULL;
✅ IS NULL
SQL
SELECT p.name
FROM reviews r
RIGHT JOIN products p
  ON r.product_id = p.id
WHERE r.id IS NULL;

Nothing is equal to NULL, so = NULL is never true. Use IS NULL.

4. COUNT(*) on a RIGHT JOIN

❌ Empty departments show 1
SQL
SELECT d.name, COUNT(*)
FROM employees e
RIGHT JOIN departments d
  ON e.department_id = d.id
GROUP BY d.name;
✅ Count a left-table column
SQL
SELECT d.name, COUNT(e.id)
FROM employees e
RIGHT JOIN departments d
  ON e.department_id = d.id
GROUP BY d.name;

An unmatched right row is still one row. Counting a column from the left table skips its NULL and gives 0.

5. Mixing LEFT and RIGHT joins in one query

❌ Hard to tell which table is complete
SQL
SELECT u.id, o.id, p.id
FROM orders o
RIGHT JOIN users u  ON o.user_id = u.id
LEFT JOIN payments p ON p.order_id = o.id;
✅ One direction throughout
SQL
SELECT u.id, o.id, p.id
FROM users u
LEFT JOIN orders o   ON o.user_id = u.id
LEFT JOIN payments p ON p.order_id = o.id;

Both queries return the same rows, but the second reads top to bottom: start with every customer, then add what exists.

RIGHT JOIN Performance

A RIGHT JOIN costs the same as the equivalent LEFT JOIN. Database optimisers treat the two as the same operation, so choosing between them is a readability decision, not a speed one. What matters is the usual join advice:

TipWhy it helps
Index the join column of the left tableFor each right row the database looks up matches there. Foreign keys such as orders.user_id usually need an index.
Filter the right table in WHEREFewer rows to keep means fewer lookups, and it does not change the join's meaning.
Select only the columns you needSELECT * across several joins returns far more data than a report uses.

RIGHT JOIN Cheat Sheet

GoalPattern
All right rows, with matches if anyFROM a RIGHT JOIN b ON a.b_id = b.id
Right rows with no match… RIGHT JOIN b ON … WHERE a.id IS NULL
Count matches, zeros includedCOUNT(a.id) with GROUP BY b.id
Filter which left rows may matchAdd the condition to ON, not WHERE
Rewrite as LEFT JOINFROM b LEFT JOIN a ON a.b_id = b.id
Keep unmatched rows from both tablesFULL OUTER JOIN

Practice RIGHT JOIN Queries

7 exercises on the real Playground tables.

Write each query yourself first. Every solution has been run against the Playground database.

  1. 1Easy Using RIGHT JOIN, list customers 10 to 20 with the id and status of each of their orders. Customers with no orders must appear. Table: orders, users

    Show solution
    SQLite · runs in the Playground
    SELECT u.id AS user_id, u.first_name,
           o.id AS order_id, o.status
    FROM orders o
    RIGHT JOIN users u
      ON o.user_id = u.id
    WHERE u.id BETWEEN 10 AND 20
    ORDER BY u.id, o.id;

    ▶ Run this query in the SQL Playground

    users is on the right, so all eleven customers are kept; the filter on the right table is safe in WHERE. The result has 14 rows.

  2. 2Easy Using RIGHT JOIN, find any product that has never been ordered. Table: order_items, products

    Show solution
    SQLite · runs in the Playground
    SELECT p.id, p.name, p.category
    FROM order_items oi
    RIGHT JOIN products p
      ON oi.product_id = p.id
    WHERE oi.id IS NULL;

    ▶ Run this query in the SQL Playground

    RIGHT JOIN keeps every product and oi.id IS NULL leaves only those with no order item: 1 product.

  3. 3Medium Rewrite this as a RIGHT JOIN that returns the same rows: users LEFT JOIN orders, showing each customer id and their number of orders, for customers 1 to 8. Table: users, orders

    Show solution
    SQLite · runs in the Playground
    SELECT u.id, COUNT(o.id) AS orders
    FROM orders o
    RIGHT JOIN users u
      ON o.user_id = u.id
    WHERE u.id <= 8
    GROUP BY u.id
    ORDER BY u.id;

    ▶ Run this query in the SQL Playground

    Swap the two tables and change LEFT to RIGHT. The ON condition, the columns and the grouping stay the same.

  4. 4Medium Using RIGHT JOIN, count the orders that have no payment at all. Table: payments, orders

    Show solution
    SQLite · runs in the Playground
    SELECT COUNT(*) AS unpaid_orders
    FROM payments p
    RIGHT JOIN orders o
      ON p.order_id = o.id
    WHERE p.id IS NULL;

    ▶ Run this query in the SQL Playground

    After the IS NULL filter every remaining row is one order with no payment, so COUNT(*) is right here.

  5. 5Medium For each product category, count the products that have no reviews. Use RIGHT JOIN. Table: reviews, products

    Show solution
    SQLite · runs in the Playground
    SELECT p.category,
           COUNT(*) AS products_without_reviews
    FROM reviews r
    RIGHT JOIN products p
      ON r.product_id = p.id
    WHERE r.id IS NULL
    GROUP BY p.category
    ORDER BY products_without_reviews DESC, p.category;

    ▶ Run this query in the SQL Playground

    The anti-join leaves one row per unreviewed product; grouping by category counts them. 10 categories have at least one.

  6. 6Challenging For every department, count the employees hired in 2024. Departments with none must show 0. Use RIGHT JOIN. Table: employees, departments

    Show solution
    SQLite · runs in the Playground
    SELECT d.name AS department,
           COUNT(e.id) AS hired_2024
    FROM employees e
    RIGHT JOIN departments d
      ON e.department_id = d.id
     AND e.hire_date >= '2024-01-01'
     AND e.hire_date <  '2025-01-01'
    GROUP BY d.id, d.name
    ORDER BY hired_2024 DESC, d.name;

    ▶ Run this query in the SQL Playground

    The date range is a condition on the left table, so it goes in ON. In WHERE it would remove departments with no 2024 hire.

  7. 7Challenging Find the customers who ordered in 2024 but not in 2023. Join the 2023 buyers to the 2024 buyers with a RIGHT JOIN. Table: orders

    Show solution
    SQLite · runs in the Playground
    WITH y2023 AS (
      SELECT DISTINCT user_id FROM orders
      WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01'
    ), y2024 AS (
      SELECT DISTINCT user_id FROM orders
      WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
    )
    SELECT b.user_id
    FROM y2023 a
    RIGHT JOIN y2024 b
      ON a.user_id = b.user_id
    WHERE a.user_id IS NULL
    ORDER BY b.user_id;

    ▶ Run this query in the SQL Playground

    The RIGHT JOIN keeps every 2024 buyer; a.user_id IS NULL keeps those with no 2023 match. 37 customers are new in 2024.

📝 Quiz — RIGHT JOIN

Test your understanding · 0/10 answered

Question 1 of 10

What does a RIGHT JOIN return?

Question 2 of 10

Which query returns the same rows as A RIGHT JOIN B?

Question 3 of 10

In orders RIGHT JOIN users, where do the NULLs appear for a customer with no orders?

Question 4 of 10

Is RIGHT OUTER JOIN different from RIGHT JOIN?

Question 5 of 10

Which join keeps unmatched rows from both tables?

Question 6 of 10

reviews r RIGHT JOIN products p … WHERE r.id IS NULL returns:

Question 7 of 10

payments p RIGHT JOIN orders o … WHERE p.status = 'success'. What happens to orders with no payment?

Question 8 of 10

In A RIGHT JOIN B RIGHT JOIN C, which table is guaranteed to be complete?

Question 9 of 10

Is a RIGHT JOIN slower than the equivalent LEFT JOIN?

Question 10 of 10

Why do most teams prefer LEFT JOIN to RIGHT JOIN?

RIGHT JOIN Interview Questions

Q1. What is a RIGHT JOIN?

A join that returns every row from the right (second) table and the matching rows from the left table. Right rows with no match are still returned, with NULL in the left table's columns. RIGHT OUTER JOIN is the same join.

Q2. What is the difference between LEFT JOIN and RIGHT JOIN?

Only the side that is kept. LEFT JOIN keeps every row of the first table, RIGHT JOIN every row of the second. A RIGHT JOIN B is equivalent to B LEFT JOIN A.

Q3. How do you rewrite a RIGHT JOIN as a LEFT JOIN?

Swap the two tables and change the keyword. The ON condition and the rest of the query stay as they are.

Q4. What is the difference between RIGHT JOIN and INNER JOIN?

INNER JOIN returns only matching rows. RIGHT JOIN also returns the right-table rows that have no match, with NULLs on the left.

Q5. What is the difference between RIGHT JOIN and FULL OUTER JOIN?

RIGHT JOIN keeps unmatched rows of the right table only. FULL OUTER JOIN keeps unmatched rows of both tables, so it returns everything a LEFT JOIN and a RIGHT JOIN would return between them.

Q6. How do you find rows in the right table that have no match?

RIGHT JOIN and filter with WHERE left_table.id IS NULL, testing a column that cannot be NULL in a real row. For example, departments with no employees: FROM employees e RIGHT JOIN departments d ON e.department_id = d.id WHERE e.id IS NULL.

Q7. Why is RIGHT JOIN rarely used?

Because it adds nothing a LEFT JOIN cannot do, and queries are easier to read when the complete table always comes first. Many style guides ask for LEFT JOIN only.

Q8. Where does a filter on the left table go in a RIGHT JOIN?

In the ON clause. In WHERE it would fail for the unmatched right rows, whose left columns are NULL, and remove them.

Q9. Can a RIGHT JOIN return more rows than the right table has?

Yes. A right row with several matching left rows is returned once per match. It never returns fewer rows than the right table unless WHERE removes them.

Q10. How do you join three tables with RIGHT JOIN?

Chain the joins: A RIGHT JOIN B ON … RIGHT JOIN C ON …. They are evaluated left to right, so the last table is the one guaranteed to be complete.

Q11. Do all databases support RIGHT JOIN?

MySQL, PostgreSQL, SQL Server and Oracle do. SQLite added RIGHT and FULL OUTER JOIN in version 3.39; before that the workaround was a LEFT JOIN with the tables swapped.

Q12. How would you use RIGHT JOIN to audit data?

Join the parent table to the child with the child on the right and keep rows where the parent key IS NULL. Those are child rows, such as orders, whose parent, such as a customer, does not exist.

Frequently Asked Questions

What is RIGHT JOIN in SQL?

RIGHT JOIN returns every row from the right table and the matching rows from the left table. When a right row has no match, it is still returned, with NULL in the left table's columns.

What is the difference between LEFT JOIN and RIGHT JOIN?

LEFT JOIN keeps every row of the first table; RIGHT JOIN keeps every row of the second. They are mirror images, and A RIGHT JOIN B returns the same rows as B LEFT JOIN A.

Is RIGHT JOIN the same as RIGHT OUTER JOIN?

Yes. RIGHT JOIN is the short form of RIGHT OUTER JOIN. The OUTER keyword is optional and does not change the result.

When should I use RIGHT JOIN instead of LEFT JOIN?

Almost never by necessity, since any RIGHT JOIN can be written as a LEFT JOIN. It is convenient when you add a table that must be complete to the end of an existing query, and you need to understand it to read other people's SQL.

Why does RIGHT JOIN return NULL values?

A right-table row with no matching left-table row has no data to show for the left columns, so they are NULL. That NULL is how you detect rows with no match.

How do I find unmatched rows with RIGHT JOIN?

Add WHERE left_table.id IS NULL after the join. Only the right-table rows that found no match remain.

Does SQLite support RIGHT JOIN?

Yes, from SQLite 3.39, released in 2022. Earlier versions support only LEFT JOIN, so the tables have to be swapped.

Is RIGHT JOIN slower than LEFT JOIN?

No. They are the same operation written from opposite sides, and the database plans them the same way.