A B
Database

SQL Joins Explained with Practical Examples

Mayur Dabhi
Mayur Dabhi
February 25, 2026
20 min read

If you've ever worked with relational databases, you've encountered SQL JOINs. They're the backbone of querying data from multiple tables, and mastering them is essential for any developer working with databases. Yet, JOINs often confuse beginners—and even intermediate developers sometimes struggle to pick the right type.

In this comprehensive guide, we'll break down every type of SQL JOIN with visual diagrams, clear explanations, and practical examples you can run immediately. By the end, you'll confidently write complex queries that combine data from multiple tables.

Why JOINs Matter

In normalized databases, data is split across multiple tables to avoid redundancy. JOINs are how you reassemble that data for meaningful queries. Understanding JOINs is the difference between struggling with SQL and mastering it.

Setting Up Our Sample Data

Before diving into JOIN types, let's create two simple tables that we'll use throughout this tutorial. We'll work with employees and departments tables—a classic example that clearly demonstrates how JOINs work.

SQL - Create Tables
-- Create departments table
CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(50) NOT NULL,
    location VARCHAR(100)
);

-- Create employees table
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(100) NOT NULL,
    dept_id INT,
    salary DECIMAL(10, 2),
    hire_date DATE
);

-- Insert sample data
INSERT INTO departments VALUES
    (1, 'Engineering', 'Building A'),
    (2, 'Marketing', 'Building B'),
    (3, 'Sales', 'Building C'),
    (4, 'HR', 'Building D');

INSERT INTO employees VALUES
    (101, 'Alice Johnson', 1, 85000, '2022-03-15'),
    (102, 'Bob Smith', 1, 75000, '2023-01-10'),
    (103, 'Carol Davis', 2, 65000, '2021-06-20'),
    (104, 'David Wilson', 3, 70000, '2020-09-05'),
    (105, 'Eve Martinez', NULL, 60000, '2024-02-01'),
    (106, 'Frank Brown', 1, 90000, '2019-11-30');
employees
emp_idemp_namedept_id
101Alice1
102Bob1
103Carol2
104David3
105EveNULL
106Frank1
departments
dept_iddept_name
1Engineering
2Marketing
3Sales
4HR

Notice some key details in our data:

These edge cases will help us clearly see the differences between JOIN types.

The JOIN Types Overview

SQL provides several types of JOINs, each serving different purposes. Here's a quick visual overview before we dive deep into each one:

SQL JOIN Types - Visual Reference INNER JOIN Only matching rows LEFT JOIN All from left + matches RIGHT JOIN All from right + matches FULL OUTER All rows from both CROSS JOIN Cartesian product SELF JOIN Table joined to itself Legend Table A (Left) Table B (Right)

Visual comparison of all SQL JOIN types

INNER JOIN - The Most Common JOIN

INNER JOIN returns only the rows where there's a match in both tables. If a row in the left table has no matching row in the right table (or vice versa), it's excluded from the results.

INNER JOIN Visualization employees departments RESULT Eve (no dept) HR (no employees)
SQL - INNER JOIN
SELECT 
    e.emp_id,
    e.emp_name,
    d.dept_name,
    d.location
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
Result (5 rows)
emp_idemp_namedept_namelocation
101Alice JohnsonEngineeringBuilding A
102Bob SmithEngineeringBuilding A
103Carol DavisMarketingBuilding B
104David WilsonSalesBuilding C
106Frank BrownEngineeringBuilding A
What's Missing?
  • Eve Martinez is excluded because she has NULL dept_id
  • HR department is excluded because no employees belong to it

LEFT JOIN (LEFT OUTER JOIN)

LEFT JOIN returns all rows from the left table and the matched rows from the right table. If there's no match, the result contains NULL values for columns from the right table.

LEFT JOIN Visualization ALL employees departments FROM
SQL - LEFT JOIN
SELECT 
    e.emp_id,
    e.emp_name,
    d.dept_name,
    d.location
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
Result (6 rows - all employees)
emp_idemp_namedept_namelocation
101Alice JohnsonEngineeringBuilding A
102Bob SmithEngineeringBuilding A
103Carol DavisMarketingBuilding B
104David WilsonSalesBuilding C
105Eve MartinezNULLNULL
106Frank BrownEngineeringBuilding A

Notice that Eve Martinez now appears in the result, but with NULL values for department columns since she has no department assigned.

Finding Unmatched Records

LEFT JOIN is perfect for finding records that don't have a match:

-- Find employees without a department
SELECT e.emp_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
WHERE d.dept_id IS NULL;

RIGHT JOIN (RIGHT OUTER JOIN)

RIGHT JOIN is the opposite of LEFT JOIN. It returns all rows from the right table and the matched rows from the left table. Unmatched left table columns contain NULL.

SQL - RIGHT JOIN
SELECT 
    e.emp_id,
    e.emp_name,
    d.dept_name,
    d.location
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
Result (6 rows - all departments)
emp_idemp_namedept_namelocation
101Alice JohnsonEngineeringBuilding A
102Bob SmithEngineeringBuilding A
106Frank BrownEngineeringBuilding A
103Carol DavisMarketingBuilding B
104David WilsonSalesBuilding C
NULLNULLHRBuilding D

Now we see the HR department in results (with NULL employee data), but Eve is excluded since she doesn't match any department.

RIGHT JOIN Best Practice

Most developers prefer LEFT JOIN and simply swap the table order. This improves readability since we read SQL left-to-right. The following queries are equivalent:

-- These produce identical results:
SELECT * FROM A RIGHT JOIN B ON A.id = B.id;
SELECT * FROM B LEFT JOIN A ON B.id = A.id;

FULL OUTER JOIN

FULL OUTER JOIN returns all rows from both tables. Where there's a match, the row is combined. Where there's no match, the missing side contains NULL values.

FULL OUTER JOIN Visualization ALL ALL MATCHED
SQL - FULL OUTER JOIN
-- MySQL doesn't support FULL OUTER JOIN directly
-- Use UNION of LEFT and RIGHT JOINs:

SELECT 
    e.emp_id,
    e.emp_name,
    d.dept_name,
    d.location
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id

UNION

SELECT 
    e.emp_id,
    e.emp_name,
    d.dept_name,
    d.location
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
Result (7 rows - all records from both tables)
emp_idemp_namedept_namelocation
101Alice JohnsonEngineeringBuilding A
102Bob SmithEngineeringBuilding A
103Carol DavisMarketingBuilding B
104David WilsonSalesBuilding C
105Eve MartinezNULLNULL
106Frank BrownEngineeringBuilding A
NULLNULLHRBuilding D

CROSS JOIN - The Cartesian Product

CROSS JOIN returns the Cartesian product of both tables—every row from the first table combined with every row from the second table. If table A has 6 rows and table B has 4 rows, the result has 6 × 4 = 24 rows.

CROSS JOIN - Cartesian Product employees Alice Bob Carol David Eve Frank departments Engineering Marketing Sales HR 6 × 4 = 24 rows
SQL - CROSS JOIN
-- Explicit CROSS JOIN syntax
SELECT 
    e.emp_name,
    d.dept_name
FROM employees e
CROSS JOIN departments d;

-- Alternative implicit syntax (comma-separated)
SELECT 
    e.emp_name,
    d.dept_name
FROM employees e, departments d;
Use CROSS JOIN Carefully!

CROSS JOINs can produce massive result sets. A CROSS JOIN between a 1,000-row table and a 1,000-row table produces 1,000,000 rows. Common legitimate uses include:

  • Generating test data
  • Creating date/product combinations for reports
  • Producing all possible pairs for analysis

SELF JOIN - Joining a Table to Itself

A SELF JOIN is when a table is joined with itself. This is useful for hierarchical data (like org charts) or comparing rows within the same table.

Let's modify our employees table to include a manager_id column:

SQL - Self Join Example
-- Add manager relationship
ALTER TABLE employees ADD COLUMN manager_id INT;

UPDATE employees SET manager_id = 106 WHERE emp_id IN (101, 102);
UPDATE employees SET manager_id = 103 WHERE emp_id = 104;

-- Self join to find employees and their managers
SELECT 
    e.emp_name AS employee,
    m.emp_name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;
Result - Employee-Manager Relationships
employeemanager
Alice JohnsonFrank Brown
Bob SmithFrank Brown
Carol DavisNULL
David WilsonCarol Davis
Eve MartinezNULL
Frank BrownNULL

Multiple JOINs - Combining More Than Two Tables

Real-world queries often join three or more tables. You simply chain JOIN clauses together:

SQL - Multiple JOINs
-- Add a projects table
CREATE TABLE projects (
    project_id INT PRIMARY KEY,
    project_name VARCHAR(100),
    dept_id INT
);

INSERT INTO projects VALUES
    (1, 'Website Redesign', 2),
    (2, 'Mobile App', 1),
    (3, 'Data Analytics', 1);

-- Join three tables
SELECT 
    e.emp_name,
    d.dept_name,
    p.project_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
INNER JOIN projects p ON d.dept_id = p.dept_id
ORDER BY e.emp_name;
Multiple Table JOIN Flow employees departments projects dept_id dept_id Each JOIN creates an intermediate result that feeds the next JOIN

JOIN Types Comparison

JOIN Type Left Table Rows Right Table Rows Use Case
INNER JOIN Only matched Only matched Find related records
LEFT JOIN All rows Only matched Keep all left + find matches
RIGHT JOIN Only matched All rows Keep all right + find matches
FULL OUTER All rows All rows Combine everything
CROSS JOIN All × All All × All Generate combinations
SELF JOIN Table joined to itself Hierarchies, comparisons

Performance Tips for JOINs

1

Always Index JOIN Columns

Ensure columns used in ON clauses have indexes. Without indexes, the database must scan entire tables for matches.

2

Filter Early with WHERE

Apply WHERE conditions before JOINing when possible. This reduces the number of rows processed by the JOIN.

3

Select Only Needed Columns

Avoid SELECT *. Specify only the columns you need to reduce data transfer and memory usage.

4

Use EXPLAIN to Analyze

Prefix your query with EXPLAIN to see the query execution plan and identify performance bottlenecks.

SQL - Index Creation for JOIN Optimization
-- Create index on foreign key column
CREATE INDEX idx_emp_dept ON employees(dept_id);

-- Analyze query execution plan
EXPLAIN SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

Common JOIN Mistakes to Avoid

Mistake #1: Forgetting the ON Clause

Omitting the ON clause creates an accidental CROSS JOIN, potentially returning millions of rows:

-- ❌ WRONG - This is a CROSS JOIN!
SELECT * FROM employees e, departments d;

-- ✅ CORRECT
SELECT * FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
Mistake #2: Ambiguous Column Names

When both tables have columns with the same name, always use table aliases:

-- ❌ WRONG - Ambiguous column 'dept_id'
SELECT dept_id FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

-- ✅ CORRECT - Specify which table
SELECT e.dept_id FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
Mistake #3: Wrong JOIN Type for NULL Handling

Remember that INNER JOIN excludes rows with NULL join keys. Use LEFT JOIN if you need to keep unmatched rows.

Practice Exercise

Try These Queries

  1. Find all employees in the Engineering department
  2. List all departments and their employee count (include departments with 0 employees)
  3. Find employees who earn more than their manager
  4. List all possible employee-project assignments

View Solutions

-- 1. Engineering employees
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
WHERE d.dept_name = 'Engineering';

-- 2. Department employee counts
SELECT d.dept_name, COUNT(e.emp_id) AS employee_count
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
GROUP BY d.dept_id, d.dept_name;

-- 3. Employees earning more than their manager
SELECT e.emp_name AS employee, e.salary AS emp_salary,
       m.emp_name AS manager, m.salary AS mgr_salary
FROM employees e
INNER JOIN employees m ON e.manager_id = m.emp_id
WHERE e.salary > m.salary;

-- 4. All employee-project combinations (CROSS JOIN)
SELECT e.emp_name, p.project_name
FROM employees e
CROSS JOIN projects p;

Conclusion

SQL JOINs are fundamental to working with relational databases. Here's a quick recap:

The key to mastering JOINs is practice. Start with simple queries, visualize the Venn diagrams, and gradually work up to complex multi-table joins. Before long, you'll be writing sophisticated queries with confidence!

Further Reading
SQL MySQL Database INNER JOIN LEFT JOIN Joins
Mayur Dabhi

Mayur Dabhi

Full Stack Developer with 5+ years of experience building scalable web applications and working with complex database systems.