PostgreSQL Inner Join

INNER JOIN returns only the rows that have matching values in both joined tables, discarding anything that does not match on either side.

How INNER JOIN Works

For each row in the left table, PostgreSQL searches the right table for rows where the join condition is true. Every matching pair is combined into one output row. If a left-table row has no match at all, it is left out of the result entirely, and the same applies symmetrically to right-table rows.

Simple Inner Join

SELECT e.name, d.department_name
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;

Joining Three Tables

INNER JOIN chains naturally: each additional JOIN clause narrows the result further to rows that match across every joined table. Order rarely matters for correctness, though it can affect readability and, in large datasets, performance.

Three-Way Inner Join

SELECT o.id AS order_id, c.name AS customer, p.name AS product
FROM orders AS o
INNER JOIN customers AS c ON o.customer_id = c.id
INNER JOIN order_items AS oi ON oi.order_id = o.id
INNER JOIN products AS p ON p.id = oi.product_id;

Combining with WHERE and Aggregates

Because INNER JOIN already filters to matched rows, it pairs well with WHERE clauses for further filtering and with GROUP BY for per-entity summaries, such as counting orders per customer.

Inner Join with Aggregation

SELECT c.name, COUNT(o.id) AS order_count
FROM customers AS c
INNER JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.name
ORDER BY order_count DESC;
  • INNER JOIN is the default join when you write plain JOIN.
  • Rows without a match on either side are excluded from the output.
  • It is the right choice when you only care about entities that have a related record.
  • Chaining multiple INNER JOINs progressively narrows the result set.
Note: Use INNER JOIN when a missing relationship should mean 'exclude this row', such as listing only customers who have placed at least one order.
Note: If your row count drops unexpectedly after adding a join, that's usually INNER JOIN doing exactly what it's supposed to: removing unmatched rows. A LEFT JOIN would keep them instead.