FULL OUTER JOIN

PostgreSQL FULL OUTER JOIN returns all rows from both tables, with NULLs where no match exists on either side. Learn syntax, examples, and common use cases.

5 min read · Last updated: March 2026 · Back to overview

Quick Answer

PostgreSQL FULL OUTER JOIN (also written FULL JOIN) returns all rows from both tables. Rows that match appear combined; rows that have no match on the other side appear with NULLs in the columns from the unmatched table. It combines the results of both LEFT JOIN and RIGHT JOIN.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

Introduction to PostgreSQL FULL OUTER JOIN

The FULL OUTER JOIN combines data from two tables and returns all rows from both sides. Matching rows are combined into a single row; unmatched rows from either table appear with NULL in the columns from the other table.

Syntax:

SELECT select_list
FROM table1
FULL OUTER JOIN table2
  ON table1.column_name = table2.column_name;

FULL JOIN is an alias — the OUTER keyword is optional.

Setting up sample tables

The examples below use an employees table and a departments table:

CREATE TABLE departments (
  department_id   serial PRIMARY KEY,
  department_name VARCHAR(255) NOT NULL
);

CREATE TABLE employees (
  employee_id   serial PRIMARY KEY,
  employee_name VARCHAR(255),
  department_id INTEGER
);

INSERT INTO departments (department_name)
VALUES ('Sales'), ('Marketing'), ('HR'), ('IT'), ('Production');

INSERT INTO employees (employee_name, department_id)
VALUES
  ('Bette Nicholson',  1),
  ('Christian Gable',  1),
  ('Joe Swank',        2),
  ('Fred Costner',     3),
  ('Sandra Kilmer',    4),
  ('Julia Mcqueen',    NULL);

Basic FULL OUTER JOIN example

SELECT
  employee_name,
  department_name
FROM employees e
FULL OUTER JOIN departments d
  ON d.department_id = e.department_id;

Output:

  employee_name   | department_name
------------------+-----------------
 Bette Nicholson  | Sales
 Christian Gable  | Sales
 Joe Swank        | Marketing
 Fred Costner     | HR
 Sandra Kilmer    | IT
 Julia Mcqueen    | null
 null             | Production
(7 rows)

Julia Mcqueen has no department assignment, so department_name is NULL. Production has no employees assigned, so employee_name is NULL.

Finding unmatched rows with FULL OUTER JOIN and WHERE

Find departments with no employees:

SELECT employee_name, department_name
FROM employees e
FULL OUTER JOIN departments d
  ON d.department_id = e.department_id
WHERE employee_name IS NULL;

Output: Production with a null employee name.

Find employees not assigned to any department:

SELECT employee_name, department_name
FROM employees e
FULL OUTER JOIN departments d
  ON d.department_id = e.department_id
WHERE department_name IS NULL;

Output: Julia Mcqueen with a null department name.

FULL OUTER JOIN tips

  • Filter on IS NULL for a left-table column to find rows that exist only in the right table, and vice versa — this is the standard pattern for table-difference queries.
  • FULL JOIN and FULL OUTER JOIN are identical; omit OUTER for brevity.
  • FULL OUTER JOIN can produce large result sets — always review the row count before using results in a downstream process.
  • When both tables are large, ensure the join column is indexed on both sides to avoid nested-loop performance issues.
  • Use COALESCE(e.department_id, d.department_id) to get the non-NULL department ID regardless of which side is NULL.

Continue in Join Tables: Cross Join.

Related in this section: PostgreSQL Joins · Table Aliases · PostgreSQL INNER JOIN

Frequently Asked Questions

What does FULL OUTER JOIN return in PostgreSQL?

FULL OUTER JOIN returns three categories of rows: (1) rows that match on both sides, combined into a single row; (2) rows from the left table with no match in the right table — right-side columns are NULL; and (3) rows from the right table with no match in the left table — left-side columns are NULL. The result is the union of LEFT JOIN and RIGHT JOIN results.

What is the difference between FULL OUTER JOIN and INNER JOIN?

INNER JOIN returns only rows where a match exists on both sides. FULL OUTER JOIN returns all rows from both tables, including unmatched rows padded with NULLs. Use INNER JOIN when you only care about matched data; use FULL OUTER JOIN when you need a complete picture of both tables, including gaps.

How do I find rows that exist in one table but not the other using FULL OUTER JOIN?

Filter on NULL after the join. To find rows with no match on either side: SELECT * FROM a FULL OUTER JOIN b ON a.id = b.id WHERE a.id IS NULL OR b.id IS NULL. To find only left-unmatched rows: WHERE b.id IS NULL. To find only right-unmatched rows: WHERE a.id IS NULL.

Is FULL JOIN the same as FULL OUTER JOIN in PostgreSQL?

Yes. The OUTER keyword is optional. FULL JOIN and FULL OUTER JOIN are identical in PostgreSQL and produce the same result.

Can I use FULL OUTER JOIN to compare two tables for differences?

Yes. A FULL OUTER JOIN with IS NULL filters is a reliable way to find rows that exist in one table but not the other. This is useful for data reconciliation tasks, such as comparing a staging table to a production table to identify inserts and deletes.