MySQL Joins
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_id | name | dept_id | mentor_id |
|---|---|---|---|
| 1 | Aditi | 10 | NULL |
| 2 | Bilal | 10 | 1 |
| 3 | Chen | 20 | 1 |
| 4 | Diya | NULL | 2 |
| 5 | Eshan | 20 | 3 |
Table: departments (3 rows)
| dept_id | dept_name |
|---|---|
| 10 | Computer Science |
| 20 | Physics |
| 30 | History |
Table: courses (4 rows)
| course_id | title | dept_id |
|---|---|---|
| CS101 | Programming in Java | 10 |
| CS102 | Databases | 10 |
| HI101 | World History | 30 |
| PH101 | Mechanics | 20 |
Table: enrolments (5 rows)
| student_id | course_id | grade |
|---|---|---|
| 1 | CS101 | A |
| 1 | CS102 | B |
| 2 | CS101 | B |
| 3 | PH101 | A |
| 5 | CS102 | C |
2. How a Join Works
SELECT columns
FROM table1
[INNER | LEFT | RIGHT | CROSS] JOIN table2
ON table1.key = table2.key;
SELECT s.name, d.dept_name
FROM students s
JOIN departments d ON d.dept_id = s.dept_id;
Result: 4 rows
| name | dept_name |
|---|---|
| Aditi | Computer Science |
| Bilal | Computer Science |
| Chen | Physics |
| Eshan | Physics |
3. The Join Types at a Glance
| Join | Returns | Tutorial |
|---|---|---|
INNER JOIN | Only matching pairs | INNER JOIN |
LEFT JOIN | All left rows, plus matches (NULLs if none) | LEFT JOIN |
RIGHT JOIN | All right rows, plus matches | RIGHT JOIN |
CROSS JOIN | Every combination of rows | CROSS JOIN |
| Self join | A table joined to itself | SELF JOIN |
| FULL OUTER JOIN | Not supported directly; LEFT UNION RIGHT | below |
4. Same Tables, Different Joins
SELECT s.name, d.dept_name FROM students s INNER JOIN departments d ON d.dept_id = s.dept_id;
INNER: 4 matching pairs
| name | dept_name |
|---|---|
| Aditi | Computer Science |
| Bilal | Computer Science |
| Chen | Physics |
| Eshan | Physics |
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
| name | dept_name |
|---|---|
| Aditi | Computer Science |
| Bilal | Computer Science |
| Chen | Physics |
| Diya | NULL |
| Eshan | Physics |
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
| name | dept_name |
|---|---|
| Bilal | Computer Science |
| Aditi | Computer Science |
| Eshan | Physics |
| Chen | Physics |
| NULL | History |
5. FULL OUTER JOIN in MySQL
MySQL has no FULL OUTER JOIN. Combine a LEFT and a RIGHT join with UNION:
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
| name | dept_name |
|---|---|
| Aditi | Computer Science |
| Bilal | Computer Science |
| Chen | Physics |
| Diya | NULL |
| Eshan | Physics |
| NULL | History |
6. Joining Three or More Tables
Add one JOIN per table. Which student takes which course?
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
| name | title | grade |
|---|---|---|
| Aditi | Databases | B |
| Aditi | Programming in Java | A |
| Bilal | Programming in Java | B |
| Chen | Mechanics | A |
| Eshan | Databases | C |
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:
SELECT name, dept_name FROM students JOIN departments USING (dept_id) ORDER BY name;
Result: 4 rows
| name | dept_name |
|---|---|
| Aditi | Computer Science |
| Bilal | Computer Science |
| Chen | Physics |
| Eshan | Physics |
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:
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
| name | dept_name |
|---|---|
| Aditi | NULL |
| Bilal | NULL |
| Chen | Physics |
| Diya | NULL |
| Eshan | Physics |
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
| name | dept_name |
|---|---|
| Chen | Physics |
| Eshan | Physics |
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 = ALLon 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.