datarekha

INNER JOIN

Combining rows from two tables on a matching condition — the single most-used multi-table operation in SQL.

6 min read Beginner SQL Lesson 7 of 27

What you'll learn

  • INNER JOIN syntax, and what 'matching' really means
  • Why join keys belong in ON, not WHERE
  • Table aliases and qualifying ambiguous columns
  • Why a join can return more rows than either input table

Before you start

Real data lives spread across many tables — users in one, their orders in another — and the answers you actually need require combining them. A JOIN matches a row from one table to a row in another, based on a condition you give it.

INNER JOIN (often written just JOIN) keeps only the rows where the condition matches in both tables. No match means no output row at all — not even a NULL-filled placeholder. Picture the keys of each table as two overlapping circles; an inner join keeps only the overlap:

ABINNER JOIN — only the overlap

Matching two tables

Here are our two tables. Each order points back to a user through user_id:

users idnamecountry
1AshaIN
2BoCN
3CarlosUS
orders iduser_idproductqty
1011Pencil3
1021Laptop1
1033Coffee2
SELECT u.name, u.country, o.id AS order_id, o.product, o.qty
FROM   users  u
JOIN   orders o ON o.user_id = u.id
ORDER BY o.id;
namecountryorder_idproductqty
AshaIN101Pencil3
AshaIN102Laptop1
CarlosUS103Coffee2

Read the join as: “for each user, find the orders where o.user_id = u.id, and emit one combined row per match.” Three things to notice. Bo has vanished — she placed no orders, so the inner join drops her. Asha appears twice — she has two orders, and each match makes a row. And o.user_id = u.id is the join key, the relationship that says which rows belong together.

A couple of habits make joins readable: give each table a short alias (users u, orders o), and qualify every column with it (u.name, not name) so that a column existing in both tables is never ambiguous.

Keys go in ON, filters go in WHERE

You can write the join condition in WHERE (FROM users u, orders o WHERE o.user_id = u.id), and for an inner join the result is identical. Don’t, though — keep join keys in ON and row filters in WHERE. It states intent clearly, and (as the next lesson shows) for outer joins putting the join key in WHERE silently turns the outer join back into an inner one. A WHERE after the join still works on any column from any joined table — WHERE u.country = 'IN' would keep just Asha’s rows.

Practice

Quick check

0/3
Q1SELECT * FROM users u JOIN orders o with no ON clause — what happens?
Q2What does INNER JOIN do with a row that has no match on the other side?
Q3Table A has 5 rows, table B has 3. You INNER JOIN them on a key. The maximum number of output rows is:

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

FAQCommon questions

Questions about this lesson

What does an INNER JOIN do?

An INNER JOIN combines rows from two tables wherever the join condition matches, keeping only the matching rows from both sides. Rows with no match on either table are dropped from the result.

What's the difference between INNER JOIN and LEFT JOIN?

INNER JOIN keeps only matching rows; LEFT JOIN keeps all rows from the left table and fills NULLs where the right table has no match. Use LEFT JOIN when you need to preserve every record from your primary table.

Why am I getting duplicate rows after a join?

A join multiplies rows when the key isn't unique on one side — each left row matches several right rows. Check the key's uniqueness, aggregate to the right grain first, or tighten the condition so each match is one-to-one.

Practice this in an interview

All questions
What does a CROSS JOIN do, and when is it actually useful?

A CROSS JOIN produces the Cartesian product of two tables — every row from the left paired with every row from the right — giving M x N output rows with no join condition. It is useful for generating date spines, creating all combinations of dimension values, or populating test data grids.

How do you join tables on multiple keys, and why is the key order in a composite index important?

You combine conditions in the ON clause with AND to join on multiple columns, which is necessary when no single column is a unique identifier across both tables. For index performance, the most selective column — or the column used in equality predicates — should come first in a composite index.

Does the order of tables in a JOIN clause affect query results or performance?

Join order never affects the logical result — SQL is declarative and the engine chooses the physical join order. However, join order in the query does influence the optimiser's starting point, and in complex queries with many tables or when statistics are stale, manually reordering joins or using query hints can significantly change performance.

How do you write a non-equi join (range join), and what are the performance implications?

A non-equi join uses inequality operators (BETWEEN, >=, <, !=) in the ON clause instead of or in addition to equality. They are correct and valid SQL, but they prevent hash-join and merge-join plans, often forcing a nested-loop join that scales quadratically — so they require careful indexing or pre-filtering at scale.

When would you use a self-join, and how do you write one?

A self-join joins a table to itself, typically to compare rows within the same dataset — classic use cases are finding employee-manager relationships in a single table, detecting duplicate rows, or comparing a row to the previous/next row when window functions are unavailable.

What is a semi-join and how does it differ from an INNER JOIN in terms of output and performance?

A semi-join returns each row from the left table at most once when at least one match exists on the right, without returning any columns from the right table. An INNER JOIN can duplicate left rows when the right side has multiple matches. In SQL, semi-joins are written with EXISTS or IN subqueries.

Related lessons

Explore further

Skip to content