# Recursive Common Table Expressions Overview

Common Table Expressions (CTEs) are a standard SQL feature, and are essentially temporary named result sets. CTEs first appeared in the SQL standard in 1999, and the first implementations began appearing in 2007.

There are two kinds of CTEs:

* [Non-recursive](non-recursive-common-table-expressions-overview);
* Recursive, which this article covers.

SQL is generally poor at recursive structures.

![trees\_and\_graphs](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-2453a12d88295f64c66d30217aee4cd85cbba7cc%2Ftrees_and_graphs.png?alt=media)

CTEs permit a query to reference itself. A recursive CTE will repeatedly execute subsets of the data until it obtains the complete result set. This makes it particularly useful for handing hierarchical or tree-structured data. [max\_recursive\_iterations](../../../../../../server-management/variables-and-modes/server-system-variables#max_recursive_iterations) avoids infinite loops.

### Syntax example

[WITH RECURSIVE](with) signifies a recursive CTE. It is given a name, followed by a body (the main query) as follows:

![rcte\_syntax](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-4d968b0d809361f5c84b913cd7d16466fa0cb3fa%2Frcte_syntax.png?alt=media)

![cte\_syntax](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-6fc1b6c81bd838764f231747f989e1d078c3dfe8%2Fcte_syntax.png?alt=media)

### Computation

Given the following structure:

![rcte\_computation](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-1b770eb3532ee5b817f26b04eca0815e3d6f1abd%2Frcte_computation.png?alt=media)

First execute the anchor part of the query:

![rcte1](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-24870b6a7ada09f3e79a88bfe0454b39570389bd%2Frcte1.png?alt=media)

Next, execute the recursive part of the query:

![rcte\_computation\_2](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-cca4ad2e888f960d130085dbd2078bbd0409b2d3%2Frcte_computation_2.png?alt=media)

![](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-0ebad4ae25aebda074335fcd37be1dab1e621b6f%2Frcte_computation_2b.png?alt=media)

![](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-ddbc052a31e38ea40f5d7074b80b0523e8be5aca%2Frcte_computation_3.png?alt=media)

![](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-b8593f012ffedebe21a68bd55d098968ddf8f511%2Frcte_computation_3b.png?alt=media)

![](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-cfb8977f626282dae8ee613cd53d8dfe137ceb61%2Frcte_computation_4.png?alt=media)

### Summary

```sql
WITH RECURSIVE R AS (
  SELECT anchor_data
  UNION [all]
  SELECT recursive_part
  FROM R, ...
)
SELECT ...
```

1. Compute anchor\_data.
2. Compute recursive\_part to get the new data.
3. If (new data is non-empty) goto 2.

### CAST to avoid truncating data

As currently implemented by MariaDB and by the SQL Standard, data may be truncated if not correctly cast. It is necessary to [CAST](../../../../sql-functions/string-functions/cast) the column to the correct width if the CTE's recursive part produces wider values for a column than the CTE's nonrecursive part. Some other DBMS give an error in this situation, and MariaDB's behavior may change in future - see [MDEV-12325](https://jira.mariadb.org/browse/MDEV-12325). See the [examples below](#cast-to-avoid-data-truncation).

### Examples

#### Transitive closure - determining bus destinations

Sample data:

![tc\_1](https://2988006611-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSsmexDFPv2xG2OTyO5yV%2Fuploads%2Fgit-blob-292b1193ea063ca8c2b8246349dcaee54be94e2b%2Ftc_1.png?alt=media)

```sql
CREATE TABLE bus_routes (origin VARCHAR(50), dst VARCHAR(50));
INSERT INTO bus_routes VALUES 
  ('New York', 'Boston'), 
  ('Boston', 'New York'), 
  ('New York', 'Washington'), 
  ('Washington', 'Boston'), 
  ('Washington', 'Raleigh');
```

Now, we want to return the bus destinations with New York as the origin:

```sql
WITH RECURSIVE bus_dst as ( 
    SELECT origin as dst FROM bus_routes WHERE origin='New York' 
  UNION
    SELECT bus_routes.dst FROM bus_routes JOIN bus_dst ON bus_dst.dst= bus_routes.origin 
) 
SELECT * FROM bus_dst;
+------------+
| dst        |
+------------+
| New York   |
| Boston     |
| Washington |
| Raleigh    |
+------------+
```

The above example is computed as follows:

First, the anchor data is calculated:

* Starting from New York.
* Boston and Washington are added.

Next, the recursive part:

* Starting from Boston and then Washington.
* Raleigh is added.
* UNION excludes nodes that are already present.

#### Computing paths - determining bus routes

This time, we are trying to get bus routes such as “New York -> Washington -> Raleigh”.

Using the same sample data as the previous example:

```sql
WITH RECURSIVE paths (cur_path, cur_dest) AS (
    SELECT origin, origin FROM bus_routes WHERE origin='New York' 
  UNION
    SELECT CONCAT(paths.cur_path, ',', bus_routes.dst), bus_routes.dst 
     FROM paths
     JOIN bus_routes 
       ON paths.cur_dest = bus_routes.origin AND 
         NOT FIND_IN_SET(bus_routes.dst, paths.cur_path)
) 
SELECT * FROM paths;
+-----------------------------+------------+
| cur_path                    | cur_dest   |
+-----------------------------+------------+
| New York                    | New York   |
| New York,Boston             | Boston     |
| New York,Washington         | Washington |
| New York,Washington,Boston  | Boston     |
| New York,Washington,Raleigh | Raleigh    |
+-----------------------------+------------+
```

#### CAST to avoid data truncation

In the following example, data is truncated because the results are not specifically cast to a wide enough type:

```sql
WITH RECURSIVE tbl AS (
  SELECT NULL AS col
  UNION
  SELECT "THIS NEVER SHOWS UP" AS col FROM tbl
)
SELECT col FROM tbl
+------+
| col  |
+------+
| NULL |
|      |
+------+
```

Explicitly use [CAST](../../../../sql-functions/string-functions/cast) to overcome this:

```sql
WITH RECURSIVE tbl AS (
  SELECT CAST(NULL AS CHAR(50)) AS col
  UNION SELECT "THIS NEVER SHOWS UP" AS col FROM tbl
)  
SELECT * FROM tbl;
+---------------------+
| col                 |
+---------------------+
| NULL                |
| THIS NEVER SHOWS UP |
+---------------------+
```

<sub>_This page is licensed: CC BY-SA / Gnu FDL_</sub>

{% @marketo/form formId="4316" %}
