PostgreSQL Joins

Joins combine rows from two or more tables based on a related column, letting you query normalized data as if it were one table.

Why Joins Exist

Relational databases split data across multiple tables to avoid duplication: customers live in one table, orders in another, linked by a customer_id foreign key. Joins are how you stitch that data back together at query time, so a single result set can show a customer's name next to their order total.

The Join Family

PostgreSQL supports several join types, each with a different rule for which rows survive when a match is or is not found on both sides. Choosing the right join type is one of the most common sources of subtly wrong query results, so it pays to understand each one precisely.

  • INNER JOIN — keeps only rows that match in both tables.
  • LEFT JOIN — keeps all rows from the left table, filling unmatched right-side columns with NULL.
  • RIGHT JOIN — the mirror image of LEFT JOIN, keeping all rows from the right table.
  • FULL JOIN — keeps all rows from both tables, matched or not.
  • CROSS JOIN — pairs every row on the left with every row on the right (no ON clause).

Basic Inner Join

SELECT o.id AS order_id, c.name AS customer
FROM orders AS o
INNER JOIN customers AS c ON o.customer_id = c.id;

Join Conditions

The ON clause defines how rows from each table are matched. It is usually an equality comparison between a primary key and a foreign key, but it can be any boolean expression, including comparisons on multiple columns at once.

Join on Multiple Conditions

SELECT s.name, r.region_name
FROM sales AS s
JOIN regions AS r
  ON s.region_id = r.id
 AND s.year = r.active_year;

Filtering After a Join

SELECT p.name, c.category_name
FROM products AS p
JOIN categories AS c ON p.category_id = c.id
WHERE c.category_name = 'Electronics';
Join TypeUnmatched Left RowsUnmatched Right Rows
INNER JOINDroppedDropped
LEFT JOINKept (NULLs on right)Dropped
RIGHT JOINDroppedKept (NULLs on left)
FULL JOINKeptKept
Note: JOIN by itself is shorthand for INNER JOIN in PostgreSQL. Writing INNER JOIN explicitly is a matter of style, but it makes intent unmistakable to future readers.
Note: A missing or incorrect ON clause silently turns your join into a cross join, multiplying row counts and producing results that look plausible but are wrong. Always double check the join predicate against the foreign key relationship.

Exercise: PostgreSQL Joins

What rows does an INNER JOIN return?