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.comEvery 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