INNER JOIN in SQL
By Owen Middleton · Updated September 2026 · Examples run on PostgreSQL 17
Before this FROM and Table References, WHERE Clause and Comparison Operators
Builds toward LEFT JOIN and RIGHT JOIN, CROSS JOIN, Self-Joins, Joining Multiple Tables
What is INNER JOIN in SQL?
JOIN combines rows from two tables by matching them on a shared value.
You're working on an orders report. The orders table has customer IDs but not names. The customers table has names but not order amounts. To get customer names alongside order totals, you need information from both tables in the same result row. Most business questions are like this: the data you need is spread across multiple tables, linked by shared ID columns. JOIN is the mechanism that pulls those pieces together.
The most common pattern: one table has a column that points to another table's records. The orders table stores a customer_id for each order. The customers table stores the full customer record. To get the customer's name alongside each order, you join on that shared ID.
How do you write an INNER JOIN in SQL?
Here's what that looks like:
SELECT c.name AS customer_name, o.id AS order_id, o.total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.idSQL scans every row in orders, finds the matching row in customers where customers.id equals that order's customer_id, and assembles one combined row. You get columns from both tables in a single result.
The ON clause defines the match condition. It almost always connects a foreign key in one table to a primary key in another. When there is no matching row on either side, that row is excluded from the result. Only rows with a match on both sides come through.
Is JOIN the same as INNER JOIN in SQL?
JOIN and INNER JOIN are the same thing. SQL lets you write either — the word INNER is optional and doesn't change the behaviour. You'll see both in the wild.
Why do you need table aliases in a SQL join?
Table aliases keep the query readable. Both tables have an id column. Without aliases, SQL wouldn't know which one you mean. o.id refers to the orders ID; c.id refers to the customers ID. The alias goes right after the table name in FROM: FROM orders o.
You can join any tables that share a meaningful relationship:
SELECT p.name AS product_name, oi.quantity, oi.unit_price
FROM order_items oi
JOIN products p ON oi.product_id = p.idEvery order line item gets the product name attached. Line items with no matching product are dropped.
Filter the joined result with WHERE exactly as you would on a single table:
SELECT c.name AS customer_name, o.total_amount FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.country = 'US'
WHERE applies after the join. The two tables are combined first, then the filter removes everyone who doesn't match.
Why does my INNER JOIN return fewer rows than expected?
The one thing that trips people up: INNER JOIN silently drops rows with no match.
An order whose customer_id doesn't exist in the customers table disappears from the result. No error, no warning. Just fewer rows than you expected. When a result is smaller than it should be, a missing match is usually the reason. The typical fix is a LEFT JOIN, which keeps unmatched rows from the left table and fills the right-side columns with NULL. That's the next topic.
An orders table has 200 rows. After INNER JOIN to customers, you get 185 rows. What does this mean?
Practice INNER JOIN in SQL
Brightlane's customer service team is assembling a case-management report and needs order records matched to the customers who placed them.
Write a query to return the customer name, order ID, and order total for every order.
Assumptions:
- Every order has a valid
customer_id, so every order will appear in the result.
Output:
- One row per order, with columns
customer_name,order_id, andtotal_amount.
Schema · ecommerce5 tables? = nullable
Run previews · Check grades
Write a query, then run it to see results here.
The full breakdown walks through the shape, each clause, why this approach beats the alternatives, and the trap to avoid.
See the full worked solutionStation Zero, our free browser SQL game, teaches this concept inside a story. No signup.
solve a JOIN as a puzzle in Station Zero9 INNER JOIN practice problems
Write a query to return the customer name, order ID, and order total for every order.
Write a query to return one row per employee with the employee's name and their department's name.
Write a query to return one row per order line item with the matching product name plus the line's quantity and unit price.
Write a query to return the customer name and order total for every order placed by a US-based customer.
Write a query to return the session ID and account plan for every session on record.
Write a query to return each employee's name and department name for every employee assigned to that department.
Write a query to return the product name and quantity for every order line item whose product is assigned to category 6.
Write a query that returns the product name and category name for every product whose category_id matches a row in categories.
Write a query to return one row per salary record, showing the employee's name alongside the salary amount and end date for that record.
Start learning to practice all 9 INNER JOIN problems, with instant grading and mastery tracking.
Common questions about INNER JOIN
What is an INNER JOIN in SQL?
An INNER JOIN combines rows from two tables by matching them on a condition, returning one row for every pair where the condition is true. Rows in either table with no match on the other side are left out. Use it when the columns you need are spread across two tables and you only want records that exist in both.
What is the difference between INNER JOIN and LEFT JOIN?
An INNER JOIN keeps only rows that match in both tables. A LEFT JOIN keeps every row from the left table and fills the right side with NULLs where there is no match. If you need orders that have no customer record to still appear, reach for a LEFT JOIN. If you only want orders that do have a matching customer, INNER JOIN is correct.
Does the order of the tables in an INNER JOIN matter?
For the rows returned, no. A INNER JOIN B and B INNER JOIN A produce the same set of matched rows, because the match condition is symmetric. Table order can affect column order when you select everything, and it changes how the query reads, but it does not change which rows survive the join.
Why does my INNER JOIN return fewer rows than the original table?
Because an INNER JOIN drops any row that has no match on the other side. If some orders point to a customer that does not exist in the customers table, those orders disappear from the result. When you expect every left-side row to appear whether or not it matches, use a LEFT JOIN instead.
Can I join more than two tables with INNER JOIN?
Yes. Chain another JOIN ON clause for each additional table, matching on a column that is already in the result so far. The query still returns only rows that have a match at every step, so each INNER JOIN you add can narrow the result further.