Docs
Docs/JOINs & Relationships/FULL OUTER JOIN

SQL FULL OUTER JOIN

Return all rows from both tables, matched where possible.

FULL OUTER JOIN returns all rows from both tables. Where there's no match, NULL values fill in the gaps.

The users and orders tables, with an extra order 104 that has no user, and the five-row FULL OUTER JOIN result
Unmatched rows from both sides are kept. Order 104 has no user, and Carol has no order.

Syntax

SELECT columns
FROM table1
FULL OUTER JOIN table2 ON table1.col = table2.col;

Example

-- Get all users AND all orders (matched where possible)
SELECT 
    u.name,
    o.id AS order_id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;

-- Imagine we have an orphan order (user_id = 99, no matching user):
-- Alice | 101
-- Alice | 102
-- Bob   | 103
-- Carol | NULL      <-- User with no orders
-- NULL  | 104       <-- Order with no user (orphan)

Run It Yourself

This example needs an order whose user does not exist. Create the tables without a foreign key so the orphan row is accepted, then add it to the same users and orders data used in the other JOIN guides.

CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);
CREATE TABLE orders (id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, total NUMERIC NOT NULL);

INSERT INTO users (id, name, email) VALUES
  (1, 'Alice', 'alice@mail.com'), (2, 'Bob', 'bob@mail.com'), (3, 'Carol', 'carol@mail.com');
INSERT INTO orders (id, user_id, total) VALUES
  (101, 1, 150), (102, 1, 75), (103, 2, 200), (104, 99, 60);

SELECT u.name, o.id AS order_id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id
ORDER BY u.id, o.id;

Output in SQLite, which sorts NULL first. PostgreSQL sorts NULL last, so its rows come back in a different order.

name   order_id
-----  --------
NULL        104
Alice       101
Alice       102
Bob         103
Carol  NULL

Which Databases Support It

FULL OUTER JOIN works in PostgreSQL, SQL Server and SQLite 3.39.0 or later. MySQL does not support it. On MySQL or an older SQLite, combine a LEFT JOIN with the orders that have no user. This returns the same five rows:

SELECT u.name, o.id AS order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id

UNION

SELECT u.name, o.id AS order_id
FROM orders o
LEFT JOIN users u ON u.id = o.user_id
WHERE u.id IS NULL;

When to use: Data reconciliation, finding orphaned records, comparing two datasets.

Need help?

Join our Discord community for support and discussions.

Join Discord