☰ Learn SQL Tutorial Menu

Outer Joins: LEFT, RIGHT, and FULL

Written by CSA mentors · Updated 16 Sept 2026 · 3 min read


INNER JOIN only keeps rows that match on both sides. Sometimes you specifically want to see the rows that don't have a match — that's what outer joins are for.

LEFT JOIN

Keeps every row from the left (first) table, matching in data from the right table where it exists, and filling in NULL where it doesn't:

SELECT c.customer_name, o.id AS order_id
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id;

Every customer appears at least once — even customers who have never placed an order (their order_id shows as NULL).

Finding rows with no match

A classic use of LEFT JOIN — combine it with WHERE ... IS NULL to find "customers who never ordered anything":

SELECT c.customer_name
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL;

RIGHT JOIN

The mirror image of LEFT JOIN — keeps every row from the right table instead. In practice, most people just flip the table order and use LEFT JOIN instead, since it reads more naturally — and SQLite doesn't support RIGHT JOIN at all.

-- These two queries return the same rows
SELECT * FROM orders o RIGHT JOIN customers c ON o.customer_id = c.id;
SELECT * FROM customers c LEFT JOIN orders o ON c.id = o.customer_id;

FULL OUTER JOIN

Keeps every row from both tables, matched where possible, NULL-filled where not. Supported in PostgreSQL and SQL Server, but not in SQLite or MySQL (you'd emulate it with a UNION of a LEFT JOIN and a RIGHT JOIN).

-- PostgreSQL / SQL Server only
SELECT c.customer_name, o.id AS order_id
FROM customers c
FULL OUTER JOIN orders o ON c.id = o.customer_id;

Quick reference

Join typeKeeps
INNER JOINOnly matching rows on both sides
LEFT JOINAll left rows, matched or not
RIGHT JOINAll right rows, matched or not
FULL OUTER JOINAll rows from both sides

Open the Code Playground, pick any SQL engine (SQLite, PostgreSQL, MySQL, or SQL Server), and paste these queries in directly — the practice database above is already loaded for you, every time.

Lesson 11 of 27

Sign in to track your progress and earn learning points for every lesson you finish.

Example

-- A ready-to-query practice database is loaded automatically before every run —
-- no CREATE TABLE needed. Available tables:
--   departments  (id, name, location)
--   employees    (id, first_name, last_name, email, department_id, job_title, salary, hire_date, manager_id)
--   customers    (id, customer_name, city, country, signup_date)
--   products     (id, product_name, category, price, stock_quantity)
--   orders       (id, customer_id, employee_id, order_date, status)
--   order_items  (id, order_id, product_id, quantity, unit_price)

SELECT * FROM employees WHERE department_id = 1;
Try it Yourself »