SQL Tutorial › Joins & Set Operators › SQL Joins Overview

SQL Joins Explained

IntermediateLesson 31 of 6914 min read7 queries run on MySQL 8.4

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.

Advertisement
In this 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:

employees · 12 rows (some columns)
emp_idfirst_namedept_id
100MeeraNULL
101Asha1
102Rahul1
103Priya1
104Karan1
105Vikram2
106Neha2
107Arjun2
108Kavya3
109Rohan3
110Sneha4
111Amit1
departments · 5 rows
dept_iddept_namelocation
1EngineeringBengaluru
2SalesMumbai
3MarketingPune
4HRDelhi
5FinanceMumbai

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

JoinKeeps
INNER JOINonly rows that match in both tables
LEFT JOINall rows of the left table + matches from the right (NULL if none)
RIGHT JOINall rows of the right table + matches from the left
FULL OUTER JOINall rows of both tables (MySQL: LEFT + RIGHT with UNION)
CROSS JOINevery combination of rows (no condition)
SELF JOINa table joined to itself

INNER JOIN

SQL
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;
ResultMySQL 8.4
emp_idfirst_namedept_name
101AshaEngineering
102RahulEngineering
103PriyaEngineering
104KaranEngineering
105VikramSales
106NehaSales
107ArjunSales
108KavyaMarketing
109RohanMarketing
110SnehaHR
111AmitEngineering
11 rows in set
How this query works, step by step
  1. MySQL pairs each employee with the department whose dept_id is equal (ON d.dept_id = e.dept_id).
  2. Meera has no matching department (NULL never equals anything), so she is dropped.
  3. Finance has no matching employee, so it is dropped too.
  4. Result: 11 rows, only complete pairs.

Full lesson: INNER JOIN.

LEFT JOIN

SQL
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;
ResultMySQL 8.4
emp_idfirst_namedept_name
100MeeraNULL
101AshaEngineering
102RahulEngineering
103PriyaEngineering
104KaranEngineering
105VikramSales
106NehaSales
107ArjunSales
108KavyaMarketing
109RohanMarketing
110SnehaHR
111AmitEngineering
12 rows in set
How this query works, step by step
  1. Every row of the left table (employees, written first) is kept.
  2. Matching departments are attached as with an inner join.
  3. Meera has no match, so she stays, with NULL in dept_name.
  4. Result: 12 rows. Finance still does not appear, because it is in the right table.

Full lesson: LEFT JOIN.

SQL
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;
ResultMySQL 8.4
emp_idfirst_namedept_name
101AshaEngineering
102RahulEngineering
103PriyaEngineering
104KaranEngineering
111AmitEngineering
105VikramSales
106NehaSales
107ArjunSales
108KavyaMarketing
109RohanMarketing
110SnehaHR
NULLNULLFinance
12 rows in set
How this query works, step by step
  1. Every row of the right table (departments) is kept.
  2. Finance has no employee, so it appears once with NULL employee columns.
  3. 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:

SQL
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;
ResultMySQL 8.4
emp_idfirst_namedept_name
NULLNULLFinance
100MeeraNULL
101AshaEngineering
102RahulEngineering
103PriyaEngineering
104KaranEngineering
105VikramSales
106NehaSales
107ArjunSales
108KavyaMarketing
109RohanMarketing
110SnehaHR
111AmitEngineering
13 rows in set
How this query works, step by step
  1. The left join gives 12 rows, including Meera without a department.
  2. The right join gives 12 rows, including Finance without an employee.
  3. UNION merges 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:

SQL
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;
ResultMySQL 8.4
categorysize
ElectronicsL
ElectronicsS
FurnitureL
FurnitureS
StationeryL
StationeryS
6 rows in set

Full lesson: CROSS JOIN.

SELF JOIN

A table joined to itself, here to put each employee next to their manager:

SQL
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;
ResultMySQL 8.4
employeemanager
MeeraNULL
AshaMeera
RahulAsha
PriyaAsha
KaranRahul
VikramMeera
NehaVikram
ArjunVikram
KavyaMeera
RohanKavya
SnehaMeera
AmitAsha
12 rows in set

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?

SQL
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;
ResultMySQL 8.4
order_idcustomer_nameproduct_namequantity
1001Ananya SharmaMechanical Keyboard1
1001Ananya SharmaNotebook Pack5
1006Ishita BoseDesk Lamp1
1006Ishita BoseUSB-C Hub2
4 rows in set
How this query works, step by step
  1. Start from orders.
  2. Attach the customer of each order.
  3. Attach the order’s lines: one row per line, so an order with two lines becomes two rows.
  4. Attach each line’s product name.

Which join should I use?

QuestionJoin
Employees with their departmentINNER JOIN
All employees, department if they have oneLEFT JOIN
Departments with no employeesLEFT JOIN from departments + WHERE e.emp_id IS NULL
Everything from both sidesFULL OUTER (UNION of LEFT and RIGHT)
All combinations (e.g. sizes × colours)CROSS JOIN
Rows related to other rows of the same tableSELF JOIN
Tip

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?

2. employees LEFT JOIN departments: what happens to an employee with no department?

3. How do you write a FULL OUTER JOIN in MySQL?

4. Table A has 4 rows and table B has 3. How many rows does A CROSS JOIN B return?

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.