MySQL Inner Join

INNER JOIN returns only the rows where the joined tables have a matching value, discarding anything without a partner on both sides.

How INNER JOIN Filters Rows

INNER JOIN compares rows from two tables using the condition in the ON clause and keeps only the pairs where that condition is true. If a customer has never placed an order, INNER JOIN simply omits that customer from the result, because there's no order row to pair it with. This makes INNER JOIN the right choice whenever you only care about complete, matched relationships, such as 'orders together with the customer who placed them'.

Orders with customer names

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

Multiple Matches Multiply Rows

When the relationship is one-to-many, an INNER JOIN produces one output row for every matching pair, not one row per record on the 'one' side. Joining orders to order_items, where a single order can contain five line items, returns five rows for that order - each repeating the order's details alongside a different item. This is expected: a join operates on pairs of matching rows, not on parent records.

Every line item alongside its order

SELECT o.order_id, o.order_date, oi.product_id, oi.quantity
FROM orders o
INNER JOIN order_items oi ON oi.order_id = o.order_id;

Adding Extra Conditions

Extra filters can go in the ON clause or the WHERE clause with an INNER JOIN and, unlike with LEFT or RIGHT JOIN, the result is identical either way, since unmatched rows were never going to appear regardless. Most developers put join logic - how tables relate - in ON, and business filters - which rows to keep - in WHERE, purely for readability.

Only orders over 5000 from Delhi customers

SELECT o.order_id, c.name, o.total_amount
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
WHERE c.city = 'Delhi' AND o.total_amount > 5000;
  • Writing plain JOIN is identical to writing INNER JOIN - INNER is the default and entirely optional.
  • Any number of INNER JOINs can be chained in a single query to pull in more related tables.
  • The order tables are listed in an INNER JOIN doesn't change the result, only readability.
  • An INNER JOIN with no ON clause, or a condition that's always true, produces a cross join - every row of one table paired with every row of the other.
orders rowcustomers rowIncluded in result?
order_id=1, customer_id=5customer_id=5 existsYes
order_id=2, customer_id=9customer_id=9 does not existNo
(no order)customer_id=12, never orderedNo
Note: If an INNER JOIN returns fewer rows than expected, check for NULLs or mismatched types in the join columns first - a customer_id stored as a string in one table and an integer in another will never match.
Note: Forgetting the ON clause entirely turns an INNER JOIN into a cross join, multiplying every row in one table by every row in the other - a 1,000-row orders table joined that way against a 1,000-row customers table returns a million rows.