00:00

MySQL Joins

Last updated: 5 min read

Relational databases split data into several tables so each fact is stored once: students in one table, departments in another. A join puts them back together in a query, matching rows through a shared key such as dept_id. This page explains how joins work and introduces every join type, each of which has its own detailed tutorial.

1. Sample Tables

Table: students (5 rows)

student_idnamedept_idmentor_id
1Aditi10NULL
2Bilal101
3Chen201
4DiyaNULL2
5Eshan203

Table: departments (3 rows)

dept_iddept_name
10Computer Science
20Physics
30History

Table: courses (4 rows)

course_idtitledept_id
CS101Programming in Java10
CS102Databases10
HI101World History30
PH101Mechanics20

Table: enrolments (5 rows)

student_idcourse_idgrade
1CS101A
1CS102B
2CS101B
3PH101A
5CS102C

2. How a Join Works

How a join matches rowsEach student row is paired with the department row whose dept_id matches. Aditi and Bilal match Computer Science, Chen and Eshan match Physics. Diya has no department, and History has no students.studentsnamedept_idAditi10Bilal10Chen20DiyaNULLEshan20departmentsdept_iddept_name10Computer Science20Physics30HistoryDiya: no dept_idHistory: no studentsON students.dept_id = departments.dept_id
Figure 1: a join pairs rows whose key values match. Unmatched rows (amber) are where the join types differ.
syntax.sql
SELECT columns
FROM table1
[INNER | LEFT | RIGHT | CROSS] JOIN table2
  ON table1.key = table2.key;
query.sql
SELECT s.name, d.dept_name
FROM students s
JOIN departments d ON d.dept_id = s.dept_id;

Result: 4 rows

namedept_name
AditiComputer Science
BilalComputer Science
ChenPhysics
EshanPhysics

3. The Join Types at a Glance

The main join typesFour small Venn diagrams of students (left) and departments (right): INNER JOIN shades only the overlap; LEFT JOIN shades all of the left circle; RIGHT JOIN all of the right circle; FULL OUTER (emulated in MySQL) shades both circles.INNER JOINstudentsdeptsLEFT JOINstudentsdeptsRIGHT JOINstudentsdeptsFULL OUTER*studentsdeptsshaded = rows returned * MySQL has no FULL OUTER JOIN keyword; use LEFT JOIN UNION RIGHT JOIN
Figure 2: each join type decides what happens to rows without a match.
JoinReturnsTutorial
INNER JOINOnly matching pairsINNER JOIN
LEFT JOINAll left rows, plus matches (NULLs if none)LEFT JOIN
RIGHT JOINAll right rows, plus matchesRIGHT JOIN
CROSS JOINEvery combination of rowsCROSS JOIN
Self joinA table joined to itselfSELF JOIN
FULL OUTER JOINNot supported directly; LEFT UNION RIGHTbelow

4. Same Tables, Different Joins

query.sql
SELECT s.name, d.dept_name FROM students s INNER JOIN departments d ON d.dept_id = s.dept_id;

INNER: 4 matching pairs

namedept_name
AditiComputer Science
BilalComputer Science
ChenPhysics
EshanPhysics
query.sql
SELECT s.name, d.dept_name FROM students s LEFT JOIN departments d ON d.dept_id = s.dept_id;

LEFT: adds Diya with NULL

namedept_name
AditiComputer Science
BilalComputer Science
ChenPhysics
DiyaNULL
EshanPhysics
query.sql
SELECT s.name, d.dept_name FROM students s RIGHT JOIN departments d ON d.dept_id = s.dept_id;

RIGHT: adds History with NULL

namedept_name
BilalComputer Science
AditiComputer Science
EshanPhysics
ChenPhysics
NULLHistory

5. FULL OUTER JOIN in MySQL

MySQL has no FULL OUTER JOIN. Combine a LEFT and a RIGHT join with UNION:

full-outer.sql
SELECT s.name, d.dept_name FROM students s LEFT JOIN departments d ON d.dept_id = s.dept_id
UNION
SELECT s.name, d.dept_name FROM students s RIGHT JOIN departments d ON d.dept_id = s.dept_id;

Result: 6 rows

namedept_name
AditiComputer Science
BilalComputer Science
ChenPhysics
DiyaNULL
EshanPhysics
NULLHistory

6. Joining Three or More Tables

Add one JOIN per table. Which student takes which course?

three-tables.sql
SELECT s.name, c.title, e.grade
FROM enrolments e
JOIN students s ON s.student_id = e.student_id
JOIN courses  c ON c.course_id  = e.course_id
ORDER BY s.name, c.title;

Result: 5 rows

nametitlegrade
AditiDatabasesB
AditiProgramming in JavaA
BilalProgramming in JavaB
ChenMechanicsA
EshanDatabasesC

7. ON vs USING

When the key column has the same name in both tables, USING (col) is a shorthand and shows the column once:

query.sql
SELECT name, dept_name FROM students JOIN departments USING (dept_id) ORDER BY name;

Result: 4 rows

namedept_name
AditiComputer Science
BilalComputer Science
ChenPhysics
EshanPhysics

ON is needed when names differ or the condition is more complex. Always use table aliases (s, d) to keep column references clear.

8. Conditions in ON vs WHERE

For INNER joins it doesn't matter. For LEFT joins it does: a condition in WHERE removes unmatched rows; in ON it only limits what is matched:

query.sql
SELECT s.name, d.dept_name FROM students s LEFT JOIN departments d ON d.dept_id = s.dept_id AND d.dept_name = 'Physics';

Filter in ON: all students kept

namedept_name
AditiNULL
BilalNULL
ChenPhysics
DiyaNULL
EshanPhysics
query.sql
SELECT s.name, d.dept_name FROM students s LEFT JOIN departments d ON d.dept_id = s.dept_id WHERE d.dept_name = 'Physics';

Filter in WHERE: behaves like INNER JOIN

namedept_name
ChenPhysics
EshanPhysics

9. Join Performance Tips

  • Index the join columns, especially foreign keys (InnoDB indexes foreign key columns automatically).
  • Join on columns of the same type and character set, otherwise indexes may not be used.
  • Select only the columns you need.
  • Check the plan with EXPLAIN; type = ALL on a big table means a missing index.
  • MySQL 8 uses hash joins when no index is available, which is much faster than old nested loops.

10. Summary

  • A join combines rows of two tables whose key values match.
  • INNER keeps matches; LEFT/RIGHT also keep unmatched rows from one side; CROSS makes all combinations; a self join pairs a table with itself.
  • Emulate FULL OUTER JOIN with LEFT JOIN UNION RIGHT JOIN.
  • Use ON (or USING), aliases, and indexes on join columns.