SQL INNER JOIN – Complete Guide with Examples (2026-27)
The SQL INNER JOIN is the most commonly used JOIN in SQL. It returns only the rows where there is a matching value in both tables based on the ON condition. Rows that exist in one table but have no match in the other table are completely excluded from the result. In this beginner-friendly step-by-step guide you will learn the INNER JOIN syntax, see eight real examples — basic join, selected columns, WHERE filter, ORDER BY, three-table join, GROUP BY with aggregates, INNER JOIN vs LEFT JOIN, and a full reporting query — plus a comparison table and common mistakes to avoid.
✅ What is SQL INNER JOIN?
✅ SQL INNER JOIN returns only the rows where there is a matching value in BOTH tables based on the specified ON condition.
✅ Rows that have no match in either table are excluded from the result — no NULLs for unmatched rows.
✅ INNER JOIN and JOIN (without a qualifier) are identical — most databases default to INNER JOIN when you write just JOIN.
✅ It is the most commonly used JOIN type in real-world SQL queries.
✅ SQL INNER JOIN Syntax
SELECT table1.column1, table2.column2, ...
FROM table1
INNER JOIN table2
ON table1.common_column = table2.common_column;
✅ INNER JOIN:- Keyword that triggers the join. Can be written as just JOIN — both are identical.
✅ ON:- Specifies the condition that connects the two tables — usually a foreign key = primary key.
✅ table1.column:- Always prefix column names with the table name or alias to avoid ambiguity.
✅ With Table Aliases (recommended):-
SELECT E.column1, D.column2, ...
FROM table1 AS E
INNER JOIN table2 AS D
ON E.common_column = D.common_column;
FROM Employees INNER JOIN Department ON ... produces exactly the same result as FROM Employees JOIN Department ON .... INNER is the default JOIN type — writing just JOIN is a common shorthand.
✅ Sample Tables Used in All Examples
| EmpID | Name | DeptID | Salary |
|---|---|---|---|
| 1 | ANNI | 10 | 10000 |
| 2 | POOJA | 20 | 9000 |
| 3 | RAJ | 30 | 5000 |
| 4 | SUJAN | 40 | 8000 |
| 5 | MEENA | NULL | 12000 |
| DeptID | DeptName | Location |
|---|---|---|
| 10 | IT | Mumbai |
| 20 | HR | Delhi |
| 30 | Sales | Pune |
| 50 | Finance | Bangalore |
✅ Example 1 – Basic INNER JOIN (Two Tables)
Join Employees with Department to show each employee's department name:
✅ SELECT Query:-
SELECT E.EmpID, E.Name, D.DeptName
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID;
✅ Result Show:-
| EmpID | Name | DeptName |
|---|---|---|
| 1 | ANNI | IT |
| 2 | POOJA | HR |
| 3 | RAJ | Sales |
✅ Example 2 – INNER JOIN with Selected Columns and Aliases
Select specific columns and use descriptive aliases for cleaner output:
✅ SELECT Query:-
SELECT E.EmpID AS EmployeeID,
E.Name AS EmployeeName,
D.DeptName AS Department,
D.Location AS Office,
E.Salary AS MonthlySalary
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID;
✅ Result Show:-
| EmployeeID | EmployeeName | Department | Office | MonthlySalary |
|---|---|---|---|---|
| 1 | ANNI | IT | Mumbai | 10000 |
| 2 | POOJA | HR | Delhi | 9000 |
| 3 | RAJ | Sales | Pune | 5000 |
✅ Example 3 – INNER JOIN with WHERE Clause
Join the tables, then filter the joined result to show only IT department employees:
✅ SELECT Query:-
SELECT E.Name AS EmployeeName,
D.DeptName AS Department,
E.Salary AS MonthlySalary
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
WHERE D.DeptName = 'IT';
✅ Result Show:-
| EmployeeName | Department | MonthlySalary |
|---|---|---|
| ANNI | IT | 10000 |
✅ Another example – Filter by salary after joining:-
SELECT E.Name, D.DeptName, E.Salary
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
WHERE E.Salary > 8000;
✅ Result Show:-
| Name | DeptName | Salary |
|---|---|---|
| ANNI | IT | 10000 |
| POOJA | HR | 9000 |
✅ Example 4 – INNER JOIN with ORDER BY
Join the tables and sort the results by salary from highest to lowest:
✅ SELECT Query:-
SELECT E.Name AS EmployeeName,
D.DeptName AS Department,
E.Salary AS MonthlySalary
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
ORDER BY E.Salary DESC;
✅ Result Show:-
| EmployeeName | Department | MonthlySalary |
|---|---|---|
| ANNI | IT | 10000 |
| POOJA | HR | 9000 |
| RAJ | Sales | 5000 |
✅ Example 5 – INNER JOIN Three Tables
Chain two INNER JOINs to combine three tables. Adding a third table (Location) linked to Department:
✅ Third Table: Location
| LocID | City | Country |
|---|---|---|
| 1 | Mumbai | India |
| 2 | Delhi | India |
| 3 | Pune | India |
✅ SELECT Query – Three-Table INNER JOIN:-
SELECT E.Name AS EmployeeName,
D.DeptName AS Department,
L.City AS City,
L.Country AS Country,
E.Salary AS MonthlySalary
FROM Employees E
INNER JOIN Department D ON E.DeptID = D.DeptID
INNER JOIN Location L ON D.Location = L.City;
✅ Result Show:-
| EmployeeName | Department | City | Country | MonthlySalary |
|---|---|---|---|---|
| ANNI | IT | Mumbai | India | 10000 |
| POOJA | HR | Delhi | India | 9000 |
| RAJ | Sales | Pune | India | 5000 |
✅ Example 6 – INNER JOIN with GROUP BY and COUNT
Count the number of employees in each department using INNER JOIN and GROUP BY:
✅ SELECT Query:-
SELECT D.DeptName AS Department,
COUNT(E.EmpID) AS TotalEmployees
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
GROUP BY D.DeptName
ORDER BY TotalEmployees DESC;
✅ Result Show:-
| Department | TotalEmployees |
|---|---|
| IT | 1 |
| HR | 1 |
| Sales | 1 |
✅ Example 7 – INNER JOIN with GROUP BY and Multiple Aggregates
Generate a complete salary report per department using INNER JOIN combined with COUNT, MIN, MAX, and AVG:
✅ SELECT Query:-
SELECT D.DeptName AS Department,
COUNT(E.EmpID) AS TotalEmployees,
MIN(E.Salary) AS LowestSalary,
MAX(E.Salary) AS HighestSalary,
ROUND(AVG(E.Salary), 2) AS AvgSalary,
SUM(E.Salary) AS TotalSalaryBill
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
GROUP BY D.DeptName
ORDER BY AvgSalary DESC;
✅ Result Show:-
| Department | TotalEmployees | LowestSalary | HighestSalary | AvgSalary | TotalSalaryBill |
|---|---|---|---|---|---|
| IT | 1 | 10000 | 10000 | 10000.00 | 10000 |
| HR | 1 | 9000 | 9000 | 9000.00 | 9000 |
| Sales | 1 | 5000 | 5000 | 5000.00 | 5000 |
✅ Example 8 – INNER JOIN with HAVING Clause
Use HAVING to filter grouped results after INNER JOIN — show only departments where the average salary is above 8000:
✅ SELECT Query:-
SELECT D.DeptName AS Department,
COUNT(E.EmpID) AS TotalEmployees,
ROUND(AVG(E.Salary), 2) AS AvgSalary
FROM Employees E
INNER JOIN Department D
ON E.DeptID = D.DeptID
GROUP BY D.DeptName
HAVING AVG(E.Salary) > 8000
ORDER BY AvgSalary DESC;
✅ Result Show:-
| Department | TotalEmployees | AvgSalary |
|---|---|---|
| IT | 1 | 10000.00 |
| HR | 1 | 9000.00 |
Sales (AvgSalary = 5000) is excluded because 5000 is not greater than 8000.
WHERE AVG(E.Salary) > 8000 causes a syntax error. Correct: HAVING AVG(E.Salary) > 8000.
✅ INNER JOIN vs LEFT JOIN – Side by Side Comparison
The same query using INNER JOIN vs LEFT JOIN produces very different results — understanding the difference is essential:
| Feature | INNER JOIN | LEFT JOIN |
|---|---|---|
| Returns | Only matched rows from both tables | All rows from left table + matched right |
| Unmatched left rows | ❌ Excluded | ✅ Included (NULL for right columns) |
| Unmatched right rows | ❌ Excluded | ❌ Excluded |
| Result rows (our tables) | 3 rows (ANNI, POOJA, RAJ) | 5 rows (all employees, 2 with NULL dept) |
| Use when | You only need records that exist in both tables | You need all left records even without a match |
| NULLs in result | No NULLs from JOIN | NULL where right table has no match |
| Most common use | Standard data retrieval from related tables | Finding orphan records, audit reports |
✅ Key Points to Remember
✅ INNER JOIN returns only matching rows — rows with no match in either table are excluded.
✅ JOIN = INNER JOIN — writing just JOIN without a qualifier defaults to INNER JOIN in all major databases.
✅ Always use the ON condition — without ON, the query becomes a CROSS JOIN producing millions of rows.
✅ Always use table aliases — E.Name, D.DeptName is clearer than Name, DeptName in multi-table queries.
✅ Chain multiple INNER JOINs — add as many INNER JOINs as needed to combine three or more tables.
✅ WHERE filters after the JOIN — the join runs first, then WHERE filters the intermediate result.
✅ Use HAVING to filter grouped JOIN results — not WHERE — when using GROUP BY with aggregate conditions.
✅ NULL values never match — a NULL in the join column (like MEENA's DeptID) never satisfies the ON condition.
✅ Specify columns, not SELECT * — in JOIN queries, always list the columns you need with their table prefix.
✅ Common SQL INNER JOIN Mistakes to Avoid
Wrong:
SELECT * FROM Employees INNER JOIN Department; — most databases throw an error or perform a CROSS JOIN.Correct:
SELECT * FROM Employees E INNER JOIN Department D ON E.DeptID = D.DeptID;
Wrong:
SELECT DeptID, Name FROM Employees INNER JOIN Department ON ...; — SQL doesn't know which table's DeptID you want.Correct:
SELECT E.DeptID, E.Name, D.DeptName FROM Employees E INNER JOIN Department D ON E.DeptID = D.DeptID;
Always join on the column that logically links the tables — the foreign key in one table must match the primary key in the other. Joining on unrelated columns produces meaningless results without an error.
If the join column has NULL values (like MEENA's DeptID = NULL), those rows will NEVER match in an INNER JOIN — NULL = anything is UNKNOWN, not TRUE. Use LEFT JOIN if you need to include NULL-keyed rows.
Wrong:
WHERE AVG(E.Salary) > 8000 — syntax error when used with GROUP BY.Correct:
HAVING AVG(E.Salary) > 8000 — HAVING filters after grouping.
✅ Frequently Asked Questions (FAQ)
SELECT E.Name, D.DeptName, L.City FROM Employees E INNER JOIN Department D ON E.DeptID = D.DeptID INNER JOIN Location L ON D.Location = L.City; Each INNER JOIN adds another table. Rows must have matching values across all joined tables.SELECT E.Name, D.DeptName FROM Employees E INNER JOIN Department D ON E.DeptID = D.DeptID WHERE D.DeptName = 'IT'; WHERE is evaluated after the JOIN is complete, filtering the intermediate joined result.