SQL INNER JOIN: combine rows from two tables

In SQL with INNER JOIN, you get only the rows where the join condition matches in both tables. Use it to combine related data — such as linking records to their lookup values — when unmatched rows should be excluded from the result. Because both sides must satisfy the join predicate, any row with a NULL join key or no corresponding match is automatically filtered out.

Output:

Output will appear here...

Output:

+----------+-------------+
| employee | department  |
+----------+-------------+
| Alma     | Engineering |
| Ben      | Engineering |
| Cara     | Sales       |
+----------+-------------+
3 row(s)

How This Example Works

  1. departments holds three departments; employees holds four people, each with a dept_id foreign key. Dan’s dept_id is NULL.
  2. INNER JOIN departments d ON d.id = e.dept_id pairs each employee row with the department row whose id equals the employee’s dept_id. Only pairs where both sides match are kept.
  3. Dan has dept_id = NULL. The = operator returns unknown when either operand is NULL, so Dan never matches any department and is excluded.
  4. The HR department (id 3) has no employees pointing to it, so it also does not appear — INNER JOIN drops unmatched rows from both sides.

What Is SQL INNER JOIN?

INNER JOIN produces the intersection of two tables based on a join predicate. Logically, it keeps only pairs of rows where the ON condition evaluates to true. Physical execution varies by engine and data shape (nested loops, hash join, merge join), but the result set always contains only matching rows.

INNER JOIN vs LEFT JOIN

INNER JOINLEFT JOIN
Returns only matched rows from both tablesReturns all rows from the left table, with NULLs where no match exists
Unmatched rows are silently droppedUnmatched left rows are preserved with NULL-filled right columns
Use when missing matches mean invalid dataUse when you need to see all left-side records regardless of matches

Rule: choose INNER JOIN when every result row must have valid data from both tables. Switch to LEFT JOIN when you need to retain rows even without a match on the right.

Common Mistakes With SQL INNER JOIN

Missing or always-true join predicate

Omitting the ON clause (or using a condition that is always true) produces a cartesian product: every employee pairs with every department.

-- Wrong: cartesian product (12 rows from 4 × 3)
SELECT * FROM employees, departments;

-- Right: explicit join predicate
SELECT * FROM employees e
INNER JOIN departments d ON d.id = e.dept_id;

Non-unique join keys causing row multiplication

INNER JOIN returns one output row per matching pair. If you join to a table where the key is not unique, one row on the left can match multiple rows on the right and multiply your results.

INNER JOIN vs EXISTS

Use INNER JOIN when you need columns from both tables in the output. Use EXISTS when you only need to filter rows based on whether a related row is present. EXISTS also avoids row multiplication if the subquery can match multiple rows, and the query planner can often stop after the first match.

-- EXISTS: one row per employee that has a matching department
SELECT e.name FROM employees e
WHERE EXISTS (SELECT 1 FROM departments d WHERE d.id = e.dept_id);

Performance Notes

Index the columns used in the join predicate (dept_id and departments.id in this case). Without an index, the engine may scan the inner table many times. For large tables, verify the plan with EXPLAIN QUERY PLAN — look for “USING INDEX” or “SEARCH” instead of “SCAN”.