SQL Joins Explained
A join combines rows from two tables by matching a column they share, such as an employee’s dept_id and a department’s dept_id. This overview runs every type of join on the same two small tables, so you can see exactly which rows each one keeps and which it drops. Each join then has its own detailed lesson.
- Why data is split across tables and joined back
- What INNER, LEFT, RIGHT and FULL joins keep
- How CROSS and SELF joins differ
- How to pick the right join for a question
Why joins exist
A well-designed database stores each fact once. Department names live in departments; employees store only a dept_id. That avoids repeating “Engineering, Bengaluru” on every engineer’s row, but it means that to print an employee next to their department name you must join the two tables back together.
The two tables
To keep the comparison easy, this lesson uses a few columns of each table:
| emp_id | first_name | dept_id |
|---|---|---|
| 100 | Meera | NULL |
| 101 | Asha | 1 |
| 102 | Rahul | 1 |
| 103 | Priya | 1 |
| 104 | Karan | 1 |
| 105 | Vikram | 2 |
| 106 | Neha | 2 |
| 107 | Arjun | 2 |
| 108 | Kavya | 3 |
| 109 | Rohan | 3 |
| 110 | Sneha | 4 |
| 111 | Amit | 1 |
| dept_id | dept_name | location |
|---|---|---|
| 1 | Engineering | Bengaluru |
| 2 | Sales | Mumbai |
| 3 | Marketing | Pune |
| 4 | HR | Delhi |
| 5 | Finance | Mumbai |
Notice the two “unmatched” rows that make the joins differ:
- Meera (100) has
dept_id = NULL: she belongs to no department. - Finance (5) has no employees.
Join types at a glance
| Join | Keeps |
|---|---|
INNER JOIN | only rows that match in both tables |
LEFT JOIN | all rows of the left table + matches from the right (NULL if none) |
RIGHT JOIN | all rows of the right table + matches from the left |
FULL OUTER JOIN | all rows of both tables (MySQL: LEFT + RIGHT with UNION) |
CROSS JOIN | every combination of rows (no condition) |
SELF JOIN | a table joined to itself |
INNER JOIN
SELECT e.emp_id, e.first_name, d.dept_nameFROM employees eINNER JOIN departments d ON d.dept_id = e.dept_idORDER BY e.emp_id;| emp_id | first_name | dept_name |
|---|---|---|
| 101 | Asha | Engineering |
| 102 | Rahul | Engineering |
| 103 | Priya | Engineering |
| 104 | Karan | Engineering |
| 105 | Vikram | Sales |
| 106 | Neha | Sales |
| 107 | Arjun | Sales |
| 108 | Kavya | Marketing |
| 109 | Rohan | Marketing |
| 110 | Sneha | HR |
| 111 | Amit | Engineering |
- MySQL pairs each employee with the department whose
dept_idis equal (ON d.dept_id = e.dept_id). - Meera has no matching department (NULL never equals anything), so she is dropped.
- Finance has no matching employee, so it is dropped too.
- Result: 11 rows, only complete pairs.
Full lesson: INNER JOIN.
LEFT JOIN
SELECT e.emp_id, e.first_name, d.dept_nameFROM employees eLEFT JOIN departments d ON d.dept_id = e.dept_idORDER BY e.emp_id;| emp_id | first_name | dept_name |
|---|---|---|
| 100 | Meera | NULL |
| 101 | Asha | Engineering |
| 102 | Rahul | Engineering |
| 103 | Priya | Engineering |
| 104 | Karan | Engineering |
| 105 | Vikram | Sales |
| 106 | Neha | Sales |
| 107 | Arjun | Sales |
| 108 | Kavya | Marketing |
| 109 | Rohan | Marketing |
| 110 | Sneha | HR |
| 111 | Amit | Engineering |
- Every row of the left table (
employees, written first) is kept. - Matching departments are attached as with an inner join.
- Meera has no match, so she stays, with
NULLindept_name. - Result: 12 rows. Finance still does not appear, because it is in the right table.
Full lesson: LEFT JOIN.
RIGHT JOIN
SELECT e.emp_id, e.first_name, d.dept_nameFROM employees eRIGHT JOIN departments d ON d.dept_id = e.dept_idORDER BY d.dept_id, e.emp_id;| emp_id | first_name | dept_name |
|---|---|---|
| 101 | Asha | Engineering |
| 102 | Rahul | Engineering |
| 103 | Priya | Engineering |
| 104 | Karan | Engineering |
| 111 | Amit | Engineering |
| 105 | Vikram | Sales |
| 106 | Neha | Sales |
| 107 | Arjun | Sales |
| 108 | Kavya | Marketing |
| 109 | Rohan | Marketing |
| 110 | Sneha | HR |
| NULL | NULL | Finance |
- Every row of the right table (
departments) is kept. - Finance has no employee, so it appears once with
NULLemployee columns. - Meera disappears, because she has no department to attach to.
Full lesson: RIGHT JOIN.
FULL OUTER JOIN
MySQL has no FULL OUTER JOIN keyword. Combine a left and a right join with UNION, which also removes the rows they share:
SELECT e.emp_id, e.first_name, d.dept_nameFROM employees eLEFT JOIN departments d ON d.dept_id = e.dept_idUNIONSELECT e.emp_id, e.first_name, d.dept_nameFROM employees eRIGHT JOIN departments d ON d.dept_id = e.dept_idORDER BY emp_id;| emp_id | first_name | dept_name |
|---|---|---|
| NULL | NULL | Finance |
| 100 | Meera | NULL |
| 101 | Asha | Engineering |
| 102 | Rahul | Engineering |
| 103 | Priya | Engineering |
| 104 | Karan | Engineering |
| 105 | Vikram | Sales |
| 106 | Neha | Sales |
| 107 | Arjun | Sales |
| 108 | Kavya | Marketing |
| 109 | Rohan | Marketing |
| 110 | Sneha | HR |
| 111 | Amit | Engineering |
- The left join gives 12 rows, including Meera without a department.
- The right join gives 12 rows, including Finance without an employee.
UNIONmerges them and removes the 11 duplicate matched rows: 13 rows, with both unmatched rows present.
Full lesson: FULL OUTER JOIN.
CROSS JOIN
A cross join pairs every row of one table with every row of the other. With 3 product categories and 2 sizes you get 6 combinations:
SELECT c.category, s.sizeFROM (SELECT DISTINCT category FROM products) cCROSS JOIN (SELECT 'S' AS size UNION ALL SELECT 'L') sORDER BY c.category, s.size;| category | size |
|---|---|
| Electronics | L |
| Electronics | S |
| Furniture | L |
| Furniture | S |
| Stationery | L |
| Stationery | S |
Full lesson: CROSS JOIN.
SELF JOIN
A table joined to itself, here to put each employee next to their manager:
SELECT e.first_name AS employee, m.first_name AS managerFROM employees eLEFT JOIN employees m ON m.emp_id = e.manager_idORDER BY e.emp_id;| employee | manager |
|---|---|
| Meera | NULL |
| Asha | Meera |
| Rahul | Asha |
| Priya | Asha |
| Karan | Rahul |
| Vikram | Meera |
| Neha | Vikram |
| Arjun | Vikram |
| Kavya | Meera |
| Rohan | Kavya |
| Sneha | Meera |
| Amit | Asha |
Full lesson: SELF JOIN.
Joining more than two tables
Joins chain: each JOIN adds one more table to the rows built so far. Who bought what?
SELECT o.order_id, c.customer_name, p.product_name, oi.quantityFROM orders oJOIN customers c ON c.customer_id = o.customer_idJOIN order_items oi ON oi.order_id = o.order_idJOIN products p ON p.product_id = oi.product_idWHERE o.order_id IN (1001, 1006)ORDER BY o.order_id, p.product_name;| order_id | customer_name | product_name | quantity |
|---|---|---|---|
| 1001 | Ananya Sharma | Mechanical Keyboard | 1 |
| 1001 | Ananya Sharma | Notebook Pack | 5 |
| 1006 | Ishita Bose | Desk Lamp | 1 |
| 1006 | Ishita Bose | USB-C Hub | 2 |
- Start from
orders. - Attach the customer of each order.
- Attach the order’s lines: one row per line, so an order with two lines becomes two rows.
- Attach each line’s product name.
Which join should I use?
| Question | Join |
|---|---|
| Employees with their department | INNER JOIN |
| All employees, department if they have one | LEFT JOIN |
| Departments with no employees | LEFT JOIN from departments + WHERE e.emp_id IS NULL |
| Everything from both sides | FULL OUTER (UNION of LEFT and RIGHT) |
| All combinations (e.g. sizes × colours) | CROSS JOIN |
| Rows related to other rows of the same table | SELF JOIN |
Most teams write LEFT JOIN and simply swap the table order instead of using RIGHT JOIN. Queries read more naturally when the “main” table comes first.
Quick quiz
1. Which join returns only rows that have a match in both tables?
INNER JOIN keeps matched pairs only.
2. employees LEFT JOIN departments: what happens to an employee with no department?
LEFT JOIN keeps every row of the left table.
3. How do you write a FULL OUTER JOIN in MySQL?
MySQL has no FULL OUTER JOIN keyword.
4. Table A has 4 rows and table B has 3. How many rows does A CROSS JOIN B return?
Every row of A is paired with every row of B: 4 × 3.
SQL joins FAQ
What are the types of joins in SQL?
INNER JOIN, LEFT (OUTER) JOIN, RIGHT (OUTER) JOIN, FULL OUTER JOIN, CROSS JOIN and SELF JOIN (a table joined to itself with any of the other types).
What is the difference between INNER JOIN and LEFT JOIN?
INNER JOIN returns only rows with a match in both tables. LEFT JOIN returns every row of the left table and fills the right table’s columns with NULL where there is no match.
Does MySQL support FULL OUTER JOIN?
No. Emulate it with LEFT JOIN ... UNION ... RIGHT JOIN.
Is JOIN the same as INNER JOIN?
Yes. In MySQL JOIN on its own means INNER JOIN.