Full Stack • Java • System Design • Cloud • AI Engineering

SQL2026-06-12

SQL Joins: Complete Guide with Visual Examples

Master SQL joins including INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, and SELF JOIN with practical examples and visual diagrams

SQL Joins

📋 Table of Contents

  1. Introduction to Joins
  2. INNER JOIN
  3. LEFT JOIN
  4. RIGHT JOIN
  5. FULL OUTER JOIN
  6. CROSS JOIN
  7. SELF JOIN
  8. Multiple Joins
  9. Best Practices

Introduction to Joins

SQL Joins combine rows from two or more tables based on a related column.

┌─────────────────────────────────────────────────────────┐
│                    SQL JOIN TYPES                        │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  INNER JOIN      →  Matching rows from both tables      │
│  LEFT JOIN       →  All from left + matching from right │
│  RIGHT JOIN      →  All from right + matching from left │
│  FULL OUTER JOIN →  All rows from both tables           │
│  CROSS JOIN      →  Cartesian product of both tables    │
│  SELF JOIN       →  Table joined with itself            │
│                                                          │
└─────────────────────────────────────────────────────────┘

Sample Tables

-- Customers table
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100),
    city VARCHAR(50)
);

INSERT INTO customers VALUES
(1, 'John Doe', 'New York'),
(2, 'Jane Smith', 'Los Angeles'),
(3, 'Bob Johnson', 'Chicago'),
(4, 'Alice Brown', 'Houston');

-- Orders table
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    amount DECIMAL(10,2)
);

INSERT INTO orders VALUES
(101, 1, '2024-01-15', 250.00),
(102, 1, '2024-02-20', 150.00),
(103, 2, '2024-01-25', 300.00),
(104, 3, '2024-03-10', 450.00);

INNER JOIN

Returns only matching rows from both tables.

Visual Representation

Table A          Table B          Result
┌─────┐          ┌─────┐          ┌─────┐
│  A  │          │  B  │          │ A∩B │
│ ┌─┐ │          │ ┌─┐ │          │ ┌─┐ │
│ │█│ │    ∩     │ │█│ │    =     │ │█│ │
│ └─┘ │          │ └─┘ │          │ └─┘ │
└─────┘          └─────┘          └─────┘

Syntax

SELECT columns
FROM table1
INNER JOIN table2 ON table1.column = table2.column;

Examples

-- Basic INNER JOIN
SELECT 
    c.customer_id,
    c.name,
    o.order_id,
    o.amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;

-- Result: Only customers who have orders
-- customer_id | name        | order_id | amount
-- 1           | John Doe    | 101      | 250.00
-- 1           | John Doe    | 102      | 150.00
-- 2           | Jane Smith  | 103      | 300.00
-- 3           | Bob Johnson | 104      | 450.00

-- INNER JOIN with WHERE clause
SELECT c.name, o.order_id, o.amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE o.amount > 200;

-- INNER JOIN with aggregate
SELECT 
    c.name,
    COUNT(o.order_id) AS total_orders,
    SUM(o.amount) AS total_spent
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;

LEFT JOIN

Returns all rows from the left table and matching rows from the right table. NULL for non-matching rows.

Visual Representation

Table A          Table B          Result
┌─────┐          ┌─────┐          ┌─────┐
│  A  │          │  B  │          │  A  │
│ ┌─┐ │          │ ┌─┐ │          │ ┌█┐ │
│ │█│ │    +     │ │ │ │    =     │ │█│ │
│ └─┘ │          │ └─┘ │          │ └█┘ │
└─────┘          └─────┘          └─────┘

Syntax

SELECT columns
FROM table1
LEFT JOIN table2 ON table1.column = table2.column;

Examples

-- Basic LEFT JOIN
SELECT 
    c.customer_id,
    c.name,
    o.order_id,
    o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;

-- Result: All customers, including those without orders
-- customer_id | name         | order_id | amount
-- 1           | John Doe     | 101      | 250.00
-- 1           | John Doe     | 102      | 150.00
-- 2           | Jane Smith   | 103      | 300.00
-- 3           | Bob Johnson  | 104      | 450.00
-- 4           | Alice Brown  | NULL     | NULL

-- Find customers without orders
SELECT c.customer_id, c.name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;

-- Result: Alice Brown (customer_id = 4)

-- LEFT JOIN with COALESCE
SELECT 
    c.name,
    COALESCE(SUM(o.amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;

RIGHT JOIN

Returns all rows from the right table and matching rows from the left table. NULL for non-matching rows.

Visual Representation

Table A          Table B          Result
┌─────┐          ┌─────┐          ┌─────┐
│  A  │          │  B  │          │  B  │
│ ┌─┐ │          │ ┌─┐ │          │ ┌█┐ │
│ │ │ │    +     │ │█│ │    =     │ │█│ │
│ └─┘ │          │ └─┘ │          │ └█┘ │
└─────┘          └─────┘          └─────┘

Syntax

SELECT columns
FROM table1
RIGHT JOIN table2 ON table1.column = table2.column;

Example

-- RIGHT JOIN (less common, can be rewritten as LEFT JOIN)
SELECT 
    c.name,
    o.order_id,
    o.amount
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;

-- Equivalent LEFT JOIN (preferred)
SELECT 
    c.name,
    o.order_id,
    o.amount
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id;

FULL OUTER JOIN

Returns all rows from both tables. NULL where no match exists.

Visual Representation

Table A          Table B          Result
┌─────┐          ┌─────┐          ┌─────┐
│  A  │          │  B  │          │ A∪B │
│ ┌─┐ │          │ ┌─┐ │          │ ┌█┐ │
│ │█│ │    ∪     │ │█│ │    =     │ │█│ │
│ └─┘ │          │ └─┘ │          │ └█┘ │
└─────┘          └─────┘          └─────┘

Syntax

SELECT columns
FROM table1
FULL OUTER JOIN table2 ON table1.column = table2.column;

Example

-- FULL OUTER JOIN (PostgreSQL, SQL Server)
SELECT 
    c.customer_id,
    c.name,
    o.order_id,
    o.amount
FROM customers c
FULL OUTER JOIN orders o ON c.customer_id = o.customer_id;

-- MySQL alternative (UNION of LEFT and RIGHT)
SELECT c.customer_id, c.name, o.order_id, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
UNION
SELECT c.customer_id, c.name, o.order_id, o.amount
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;

CROSS JOIN

Returns the Cartesian product of both tables (all possible combinations).

Syntax

SELECT columns
FROM table1
CROSS JOIN table2;

Examples

-- CROSS JOIN
SELECT c.name, o.order_id
FROM customers c
CROSS JOIN orders o;

-- Result: 4 customers × 4 orders = 16 rows

-- Practical use: Generate combinations
SELECT 
    sizes.size,
    colors.color
FROM 
    (SELECT 'S' AS size UNION SELECT 'M' UNION SELECT 'L') sizes
CROSS JOIN
    (SELECT 'Red' AS color UNION SELECT 'Blue' UNION SELECT 'Green') colors;

-- Result: All size-color combinations (9 rows)

SELF JOIN

A table joined with itself, useful for hierarchical or comparative data.

Example: Employee-Manager Relationship

CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    name VARCHAR(100),
    manager_id INT
);

INSERT INTO employees VALUES
(1, 'Alice', NULL),      -- CEO
(2, 'Bob', 1),           -- Reports to Alice
(3, 'Charlie', 1),       -- Reports to Alice
(4, 'David', 2),         -- Reports to Bob
(5, 'Eve', 2);           -- Reports to Bob

-- Find employees and their managers
SELECT 
    e.name AS employee,
    m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.employee_id;

-- Result:
-- employee | manager
-- Alice    | NULL
-- Bob      | Alice
-- Charlie  | Alice
-- David    | Bob
-- Eve      | Bob

Multiple Joins

Combining data from three or more tables.

Example

-- Three-table join
SELECT 
    c.name AS customer,
    o.order_id,
    p.product_name,
    od.quantity,
    od.unit_price
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_details od ON o.order_id = od.order_id
INNER JOIN products p ON od.product_id = p.product_id;

-- Mixed join types
SELECT 
    c.name,
    o.order_id,
    COALESCE(p.product_name, 'No products') AS product
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
LEFT JOIN order_details od ON o.order_id = od.order_id
LEFT JOIN products p ON od.product_id = p.product_id;

Best Practices

1. Use Table Aliases

-- ✅ GOOD: Clear aliases
SELECT c.name, o.order_id
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;

-- ❌ BAD: No aliases
SELECT customers.name, orders.order_id
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id;

2. Specify Join Conditions Clearly

-- ✅ GOOD: Explicit ON clause
SELECT *
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id;

-- ❌ BAD: Implicit join (old style)
SELECT *
FROM orders o, customers c
WHERE o.customer_id = c.customer_id;

3. Use Appropriate Join Type

-- ✅ GOOD: LEFT JOIN when you need all left table rows
SELECT c.name, COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;

### 5. Filter Early

```sql
-- ✅ GOOD: Filter before join
SELECT c.name, o.order_id
FROM customers c
INNER JOIN (
    SELECT * FROM orders WHERE order_date >= '2024-01-01'
) o ON c.customer_id = o.customer_id;

-- ✅ ALSO GOOD: Filter in WHERE (optimizer handles it)
SELECT c.name, o.order_id
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= '2024-01-01';

Join Type Decision Tree

Need all rows from left table?
├─ YES → LEFT JOIN
└─ NO
   ├─ Need only matching rows? → INNER JOIN
   ├─ Need all rows from right table? → RIGHT JOIN
   ├─ Need all rows from both? → FULL OUTER JOIN
   └─ Need all combinations? → CROSS JOIN

Summary

This guide covered:

  • ✅ INNER JOIN for matching rows
  • ✅ LEFT JOIN for all left table rows
  • ✅ RIGHT JOIN for all right table rows
  • ✅ FULL OUTER JOIN for all rows from both tables
  • ✅ CROSS JOIN for Cartesian product
  • ✅ SELF JOIN for hierarchical data
  • ✅ Multiple joins for complex queries
  • ✅ Best practices for efficient joins

Key Takeaways:

  1. Choose the right join type for your needs
  2. Always use explicit ON clauses
  3. Index foreign key columns
  4. Use table aliases for readability
  5. Filter data early for better performance
  6. Prefer LEFT JOIN over RIGHT JOIN for consistency