AnyLearn
All lessons
Programmingbeginner

SQL JOINs: Combining Data from Multiple Tables

Learn how to combine data from different tables in a relational database using the four fundamental SQL JOIN types: INNER, LEFT, RIGHT, and FULL OUTER. We'll explore what each join does, when to use it, and how they differ, using clear examples and diagrams.

Not signed in — your progress and quiz score won't be saved.
Lesson progress1 / 10

Why Do We Need JOINs?

In a relational database, data is organized into multiple tables to reduce redundancy and improve data integrity. This is called normalization. For example, instead of storing a department's name in every single employee's record, we store a department_id in the employees table and have a separate departments table that maps that ID to the department's name, location, and other details.

This is efficient for storage, but when we need to generate a report, we want to see the employee's name and their department's name together. A JOIN clause is the SQL mechanism for combining rows from two or more tables based on a related column between them. It's the fundamental operation for retrieving meaningful, connected data from a normalized database.

Full lesson text

All 10 steps on one page — for reading, reference, and search.

Show

1. Why Do We Need JOINs?

In a relational database, data is organized into multiple tables to reduce redundancy and improve data integrity. This is called normalization. For example, instead of storing a department's name in every single employee's record, we store a department_id in the employees table and have a separate departments table that maps that ID to the department's name, location, and other details.

This is efficient for storage, but when we need to generate a report, we want to see the employee's name and their department's name together. A JOIN clause is the SQL mechanism for combining rows from two or more tables based on a related column between them. It's the fundamental operation for retrieving meaningful, connected data from a normalized database.

2. Our Example Tables

Throughout this lesson, we'll use two simple tables to illustrate how different joins work. Imagine we run a small company.

First, our employees table. Notice that David does not have a department_id.

idnamedepartment_id
1Alice1
2Bob2
3Charlie1
4DavidNULL

Second, our departments table. Notice that the 'Sales' department exists, but no employees are assigned to it yet.

idname
1Engineering
2Marketing
3Sales

The employees.department_id column is a foreign key that references the departments.id column. This relationship is what we'll use in our ON clause to connect them.

3. INNER JOIN: The Intersection

INNER JOIN is the most common join type. It selects all rows from both participating tables as long as there is a match between the columns in the ON clause. If a row in one table does not have a corresponding match in the other, it is excluded from the result. Think of it as the intersection in a Venn diagram.

flowchart TD
  subgraph "Tables"
    A["employees"]
    B["departments"]
  end
  subgraph "Result"
    C["Only rows with a match in BOTH tables"]
  end
  A -- "INNER JOIN" --> C
  B -- "INNER JOIN" --> C

4. INNER JOIN in Action

Let's write a query to get a list of employees and the name of the department they work in. We'll join employees and departments on their related ID columns.

SELECT
  employees.name AS employee_name,
  departments.name AS department_name
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;

This query produces the following result:

employee_namedepartment_name
AliceEngineering
BobMarketing
CharlieEngineering

Notice two things: David is missing because his department_id is NULL and therefore has no match in the departments table. The 'Sales' department is missing because no employee has department_id = 3, so it has no match in the employees table.

5. LEFT JOIN: All from the Left

A LEFT JOIN (or LEFT OUTER JOIN) returns all rows from the left table (employees in our example), and the matched rows from the right table (departments). If there is no match for a row from the left table, the columns from the right table will contain NULL values. This is useful when you want to see all the data from one primary table, regardless of whether it has a match in the other.

flowchart TD
  subgraph "Tables"
    A["employees (Left)"]
    B["departments (Right)"]
  end
  subgraph "Result"
    C["ALL rows from employees,
plus matches from departments"]
  end
  A -- "LEFT JOIN" --> C
  B -- " " --> C

6. LEFT JOIN in Action

Let's run the same query, but change INNER to LEFT. The "left" table is the one specified first, right after FROM.

SELECT
  employees.name AS employee_name,
  departments.name AS department_name
FROM employees
LEFT JOIN departments ON employees.department_id = departments.id;

Now the result includes every employee:

employee_namedepartment_name
AliceEngineering
BobMarketing
CharlieEngineering
DavidNULL

As you can see, David is now in our result set. Since his department_id was NULL, there was no matching id in the departments table, so department_name is NULL for his row. The 'Sales' department is still excluded because it's in the right table and had no matches.

7. RIGHT JOIN: The Mirror Image

A RIGHT JOIN (or RIGHT OUTER JOIN) is the exact opposite of a LEFT JOIN. It returns all rows from the right table (departments), and the matched rows from the left table (employees). If there's no match, the columns from the left table will be NULL.

SELECT
  employees.name AS employee_name,
  departments.name AS department_name
FROM employees
RIGHT JOIN departments ON employees.department_id = departments.id;

This gives us a result that includes every department:

employee_namedepartment_name
AliceEngineering
CharlieEngineering
BobMarketing
NULLSales

Now the 'Sales' department appears, but since no employee is assigned to it, employee_name is NULL. David is gone because he is in the left table and had no match. In practice, RIGHT JOIN is less common; developers often prefer to reorder their tables and consistently use LEFT JOIN.

8. FULL OUTER JOIN: The Whole Story

A FULL OUTER JOIN combines the results of both LEFT and RIGHT joins. It returns all rows from both tables. It will place NULL on either side of the result where a match is not found. This join can produce very large result sets and should be used with care.

SELECT
  employees.name AS employee_name,
  departments.name AS department_name
FROM employees
FULL OUTER JOIN departments ON employees.department_id = departments.id;

This query gives us everyone and every department:

employee_namedepartment_name
AliceEngineering
BobMarketing
CharlieEngineering
DavidNULL
NULLSales

Heads up: Some database systems, like MySQL, do not support FULL OUTER JOIN syntax directly. In those cases, you can emulate it by taking the UNION of a LEFT JOIN and a RIGHT JOIN.

9. Practical Trick: Finding Orphans

A very common task is to find rows in one table that don't have a match in another. For example, how can we find all employees who haven't been assigned to a department? A LEFT JOIN combined with a WHERE clause is perfect for this.

We start with a LEFT JOIN to keep all employees. We then filter this result, keeping only the rows where the column from the right table is NULL, which indicates no match was found.

SELECT employees.name
FROM employees
LEFT JOIN departments ON employees.department_id = departments.id
WHERE departments.id IS NULL;

This query would correctly return a single result: David. This pattern is extremely useful for data cleaning and integrity checks, such as finding products without categories or customers without orders.

10. JOINs at a Glance

Choosing the right join is crucial for getting the data you need. Here's a quick summary to help you decide.

Join TypeWhen to Use It
INNER JOINWhen you only want records that have a match in both tables.
LEFT JOINWhen you want all records from the left table, plus any matching data from the right.
RIGHT JOINWhen you want all records from the right table, plus any matching data from the left.
FULL OUTER JOINWhen you want all records from both tables, regardless of whether they have a match.

Most of the time, you'll find yourself reaching for INNER JOIN and LEFT JOIN. They cover the vast majority of day-to-day use cases for combining data.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Which JOIN type returns only the rows that have matching values in both tables?
    • LEFT JOIN
    • RIGHT JOIN
    • INNER JOIN
    • FULL OUTER JOIN
  2. If you perform a `LEFT JOIN` from `table_A` to `table_B`, which table will have all of its rows included in the result?
    • table_A
    • table_B
    • Neither, only matching rows are included
    • Both tables will have all rows included
  3. You want to find all departments, including those that have no employees. Which query would achieve this?
    • FROM departments INNER JOIN employees ON ...
    • FROM employees LEFT JOIN departments ON ...
    • FROM employees RIGHT JOIN departments ON ...
    • FROM employees FULL OUTER JOIN departments ON ... WHERE employees.id IS NULL
  4. An employee named 'David' has a `NULL` value in his `department_id` column. What will be the value of `departments.name` for his row in the result of a `LEFT JOIN` from `employees` to `departments`?
    • An error will occur
    • 0
    • The string 'NULL'
    • NULL
  5. What is the primary purpose of the `ON` clause in a SQL `JOIN` statement?
    • To filter the final results, similar to a WHERE clause
    • To specify the condition for matching rows between the tables
    • To rename columns in the output
    • To specify which table is the 'left' table

Related lessons