PostgreSQLINNER JOIN

INNER JOIN

An INNER JOIN returns only the rows that have a matching row on both sides of the join condition. If a row on either side has no match, it is left out of the result entirely. This is the join type you reach for by default, and the one you will use most often.
A worked example

Suppose we want every order along with the customer who placed it.

Orders inner-joined to customers

SQL
SELECT o.order_id, o.order_date, o.total_amount,
       c.first_name, c.last_name, c.email
FROM orders o
INNER JOIN customers c ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC;
order_id | order_date | total_amount | first_name | last_name | email
---------+------------+--------------+------------+-----------+---------------------
    1042 | 2024-06-02 |       129.99 | Maria      | Chen      | maria@example.com
    1041 | 2024-06-01 |        49.50 | David      | Okafor    | david@example.com
Every row in this result has a real, matching row on both sides: each order genuinely belongs to a customer that exists in the customers table. If an order somehow referenced a customer_id that no longer exists in customers, that order would simply not appear — an inner join silently drops it.
JOIN alone means INNER JOIN
Writing JOIN with no qualifier is shorthand for INNER JOIN — they are exactly the same in PostgreSQL. Many developers write plain JOIN for inner joins and reserve the explicit LEFT JOIN/RIGHT JOIN/FULL JOIN keywords for the cases where the distinction actually matters.
The ON clause
The ON clause is a boolean condition, most commonly an equality between a foreign key and the primary key it references. It does not have to be a simple equality — you can join on multiple columns or add extra conditions — but the equality-on-a-key pattern covers the vast majority of real-world joins.

Joining order_items to both orders and products at once

SQL
SELECT o.order_id, p.name, oi.quantity, oi.unit_price
FROM order_items oi
INNER JOIN orders o ON o.order_id = oi.order_id
INNER JOIN products p ON p.product_id = oi.product_id;
Chaining multiple INNER JOINs like this is completely normal — each join adds one more table’s columns into the same result set, and a row only survives if it has a match at every step.
  • INNER JOIN keeps only rows with a match on both sides

  • Plain JOIN is shorthand for INNER JOIN

  • Chaining several INNER JOINs is normal for pulling data from many related tables