How to execute recursive SELECT queries in MySQL 8

Updated: January 26, 2024 By: Guest Contributor Post a comment

Introduction to Recursive Queries

Recursive queries are an advanced SQL technique used to handle hierarchical or tree-structured data such as organizational charts, file systems, or categorization trees. Unlike traditional SQL queries, a recursive query is capable of repeating itself with changed values, thus providing a method to walk through nested data. MySQL 8 introduced the Common Table Expression (CTE) feature, including recursive CTEs, which greatly simplified the process of writing such queries.

Understanding Common Table Expressions

A Common Table Expression, or CTE, is a temporary result set which you can reference within a SELECT, INSERT, UPDATE, or DELETE statement. A recursive CTE usually includes the following parts:

  • An initial query, often referred to as the anchor member, which selects the base result set.
  • A recursive member, which references the CTE itself.
  • A UNION or UNION ALL operator to combine the initial and recursive results.

Setup for Examples

For the purpose of our examples, let’s assume we have a table employees structured as follows:

CREATE TABLE employees (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    supervisor_id INT,
    FOREIGN KEY (supervisor_id) REFERENCES employees (id)
);

This table stores employees and their respective supervisors, creating a hierarchy. Now, we’ll demonstrate how to write recursive SELECT queries step by step.

Basic Recursive Query

Let’s write a simple recursive query to select an employee and all their subordinates:

WITH RECURSIVE subordinates AS (
    SELECT id, name FROM employees WHERE name = 'Alice'
    UNION ALL
    SELECT e.id, e.name
    FROM employees e
    INNER JOIN subordinates s ON s.id = e.supervisor_id
)
SELECT * FROM subordinates;

This query will output something like:

| id  | name   |
|-----|--------|
| 1   | Alice  |
| 2   | Bob    |
| 3   | Carol  |

Handling More Complex Hierarchies

As hierarchies become more complex, you can alter the recursive query to retrieve additional levels or impose conditions. Here’s an example where we select employees down to a certain level in the hierarchy:

WITH RECURSIVE subordinates AS (
    SELECT id, name, 1 AS depth
    FROM employees
    WHERE supervisor_id IS NULL
    UNION ALL
    SELECT e.id, e.name, s.depth + 1
    FROM employees e
    JOIN subordinates s ON e.supervisor_id = s.id
    WHERE s.depth < 3
)
SELECT * FROM subordinates;

The above query only selects employees up to the 2nd level under the root supervisor.

Recursive Tree Paths

To track the path from an employee to their top-level supervisor, you can amend your query thus:

WITH RECURSIVE emp_path AS (
    SELECT id, name, CONCAT(name) AS path
    FROM employees
    WHERE supervisor_id IS NULL
    UNION ALL
    SELECT e.id, e.name, CONCAT(p.path, ' > ', e.name)
    FROM employees e
    JOIN emp_path p ON p.id = e.supervisor_id
)
SELECT * FROM emp_path;

Here each row in the resulting set includes not only the employee’s ID and name but also a text representation of their path in the hierarchy.

Advanced Usage: Recursive Aggregates

Recursive queries can also be employed for calculations such as aggregating values up or down a hierarchy. Here is an example calculating total salaries:

WITH RECURSIVE salary_cte AS (
    SELECT id, name, salary
    FROM employees
    WHERE supervisor_id IS NULL
    UNION ALL
    SELECT e.id, e.name, s.salary + e.salary
    FROM employees e
    JOIN salary_cte s ON e.supervisor_id = s.id
)
SELECT name, SUM(salary) as total_salary
FROM salary_cte
GROUP BY name;

This query computes not only the individual salaries but also accumulates it up to the hierarchy.

Handling Recursive Query Performance

Recursive queries are powerful, but they can also lead to performance issues if not managed carefully, especially with very deep or broad trees. Here are a few tips:

  • Always ensure there’s a base condition that stops the recursion.
  • Use UNION ALL instead of UNION if possible, as it’s more performant.
  • Consider limiting the depth or breadth of the tree you’re querying.

Conclusion

In this tutorial, we have explored how to craft recursive queries with MySQL 8’s CTE feature. These queries are highly flexible and can be adapted for a variety of hierarchical data patterns. By understanding the basics and practicing with the given examples, you will be able to manage complex data structures with greater ease and efficiency.