Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. SQL Dictionary
  3. INNER JOIN

INNER JOIN

Since: SQL-92(1992)

Joins two tables and retrieves only the rows that match in both. It is the most commonly used type of join.

Syntax

Use INNER JOIN to join two tables.

SELECT column FROM table1
INNER JOIN table2 ON table1.join_column = table2.join_column;

INNER is optional (JOIN alone works the same way).

SELECT column FROM table1
JOIN table2 ON table1.join_column = table2.join_column;

Alternative syntax using a WHERE clause (equivalent).

SELECT column FROM table1, table2
WHERE table1.join_column = table2.join_column;

Syntax Reference

SyntaxDescription
INNER JOIN table2 ON conditionRetrieves only the rows from both tables that satisfy the ON condition. Rows that do not exist in either table are excluded from the result.
JOIN table2 ON conditionA shorthand for INNER JOIN. The behavior is identical.
table_name AS aliasAssigns a short alias to a table. This makes queries more concise when working with multiple tables.

Tables Used in the Samples

The following sample code uses these two tables.

CREATE TABLE departments (
    id INT PRIMARY KEY,
    department_name VARCHAR(50)
);
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    salary INT
);

INSERT INTO departments VALUES (1, 'Future Gadget Lab'), (2, 'Victor Chondria University');
INSERT INTO employees VALUES
(1, 'Okabe Rintaro', 1, 300000), (2, 'Makise Kurisu', 2, 380000),
(3, 'Shiina Mayuri', 1, 280000), (4, 'Hashida Itaru', 1, 320000);

Verify the table contents.

SELECT * FROM employees;
+----+---------------+---------------+--------+
| id | name          | department_id | salary |
+----+---------------+---------------+--------+
|  1 | Okabe Rintaro |             1 | 300000 |
|  2 | Makise Kurisu |             2 | 380000 |
|  3 | Shiina Mayuri |             1 | 280000 |
|  4 | Hashida Itaru |             1 | 320000 |
+----+---------------+---------------+--------+
4 rows in set
SELECT * FROM departments;
+----+---------------------------+
| id | department_name           |
+----+---------------------------+
|  1 | Future Gadget Lab         |
|  2 | Victor Chondria University|
+----+---------------------------+
2 rows in set

Sample Code

Join the employees and departments tables to retrieve the employee list with department names.

employees id name department_id salary 1 Okabe Rintaro 1 300000 2 Makise Kurisu 2 380000 3 Shiina Mayuri 1 280000 4 Hashida Itaru 1 320000 4 rows in set

departments id department_name 1 Future Gadget Lab 2 Victor Chondria University 2 rows in set

Join the employees table and the departments table to get a list of employees with their department names.

SELECT e.name, d.department_name, e.salary
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;
+---------------+--------------------------+--------+
| name          | department_name          | salary |
+---------------+--------------------------+--------+
| Okabe Rintaro | Future Gadget Lab        | 300000 |
| Makise Kurisu | Victor Chondria University| 380000 |
| Shiina Mayuri | Future Gadget Lab        | 280000 |
| Hashida Itaru | Future Gadget Lab        | 320000 |
+---------------+--------------------------+--------+
4 rows in set

Join the employees and departments tables, and filter to employees with salary 300,000 or more.

SELECT e.name, d.department_name, e.salary
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id
WHERE e.salary >= 300000
ORDER BY e.salary DESC;
+---------------+--------------------------+--------+
| name          | department_name          | salary |
+---------------+--------------------------+--------+
| Makise Kurisu | Victor Chondria University| 380000 |
| Hashida Itaru | Future Gadget Lab        | 320000 |
| Okabe Rintaro | Future Gadget Lab        | 300000 |
+---------------+--------------------------+--------+
3 rows in set

Get the highest salary per department using GROUP BY (applied example).

SELECT d.department_name, MAX(e.salary) AS max_salary
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id
GROUP BY d.department_name;
+--------------------------+------------+
| department_name          | max_salary |
+--------------------------+------------+
| Future Gadget Lab        |     320000 |
| Victor Chondria University|     380000 |
+--------------------------+------------+
2 rows in set

Combine with a WHERE clause to filter results.

SELECT e.name, d.department_name
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id
WHERE d.department_name = 'Future Gadget Lab';
+---------------+-------------------+
| name          | department_name   |
+---------------+-------------------+
| Okabe Rintaro | Future Gadget Lab |
| Shiina Mayuri | Future Gadget Lab |
| Hashida Itaru | Future Gadget Lab |
+---------------+-------------------+
3 rows in set

Database-Specific Notes

The INNER JOIN ... ON syntax is supported across all major databases.

-- Works in MySQL, PostgreSQL, SQL Server, Oracle, and SQLite
SELECT e.name, d.department_name, e.salary
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;
+---------------+--------------------------+--------+
| name          | department_name          | salary |
+---------------+--------------------------+--------+
| Okabe Rintaro | Future Gadget Lab        | 300000 |
| Makise Kurisu | Victor Chondria University| 380000 |
| Shiina Mayuri | Future Gadget Lab        | 280000 |
| Hashida Itaru | Future Gadget Lab        | 320000 |
+---------------+--------------------------+--------+
4 rows in set

Oracle traditionally used the FROM table1, table2 WHERE condition syntax, but INNER JOIN ... ON has been available since Oracle 9i. Some prefer writing explicit JOIN syntax for readability.

INNER / LEFT / RIGHT / FULL JOIN Comparison

The type of join determines how unmatched rows are handled.

TypeMatching rowsLeft table onlyRight table onlyUse case
INNER JOINIncludedExcludedExcludedOnly rows that exist in both tables
LEFT JOINIncludedIncluded (right is NULL)ExcludedKeep all rows from the left table (e.g., users + order history)
RIGHT JOINIncludedExcludedIncluded (left is NULL)Keep all rows from the right table (often rewritable as LEFT JOIN)
FULL JOINIncludedIncluded (right is NULL)Included (left is NULL)Keep all rows from both tables (not supported in MySQL)

The following example shows the difference when an employee with department_id=99 (no matching department) exists.

-- INNER JOIN: returns only employees whose department exists.
SELECT e.name, d.department_name
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;
-- The employee with department_id=99 is excluded.

-- LEFT JOIN: returns all employees; department_name is NULL if no match.
SELECT e.name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d ON e.department_id = d.id;
-- The employee with department_id=99 has department_name = NULL.

-- RIGHT JOIN: returns all departments; name is NULL if no employees.
SELECT e.name, d.department_name
FROM employees AS e
RIGHT JOIN departments AS d ON e.department_id = d.id;
-- Departments with no employees have name = NULL.

The "LEFT JOIN + IS NULL" pattern for finding unmatched rows is often faster than NOT IN and is widely used in production.

-- Find employees who do not belong to any department.
SELECT e.name
FROM employees AS e
LEFT JOIN departments AS d ON e.department_id = d.id
WHERE d.id IS NULL;

Common Mistakes

Adding a filter condition in ON does not remove rows from a LEFT JOIN — non-matching departments are set to NULL, but the row is still returned.

SELECT e.name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d
    ON e.department_id = d.id
    AND d.department_name = 'Future Gadget Lab';

NG result (filter in ON): all rows are returned, but non-matching departments are NULL.

+---------------+-------------------+
| name          | department_name   |
+---------------+-------------------+
| Okabe Rintaro | Future Gadget Lab |
| Makise Kurisu | NULL              |
| Shiina Mayuri | Future Gadget Lab |
| Hashida Itaru | Future Gadget Lab |
+---------------+-------------------+
4 rows in set
SELECT e.name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d ON e.department_id = d.id
WHERE d.department_name = 'Future Gadget Lab';

OK result (filter in WHERE): equivalent to INNER JOIN — rows are filtered out.

+---------------+-------------------+
| name          | department_name   |
+---------------+-------------------+
| Okabe Rintaro | Future Gadget Lab |
| Shiina Mayuri | Future Gadget Lab |
| Hashida Itaru | Future Gadget Lab |
+---------------+-------------------+
3 rows in set

Overview

INNER JOIN is a core feature of relational databases, used to combine data from normalized tables. You specify the join condition in the ON clause, and only rows that satisfy the condition in both tables are returned.

The FROM table1, table2 WHERE table1.id = table2.id syntax is equivalent to INNER JOIN ... ON, but some prefer writing explicit JOIN for readability.

If you want to include rows even when there is no matching row in the other table, use LEFT JOIN / RIGHT JOIN.

If you find any errors or copyright issues, please .