Recursive Common Table Expressions have been supported since MariaDB 10.2.2.
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:
SQL is generally poor at recursive structures.
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 avoids infinite loops.
WITH RECURSIVE signifies a recursive CTE. It is given a name, followed by a body (the main query) as follows:
Given the following structure:
First execute the anchor part of the query:
Next, execute the recursive part of the query:
with recursive R as ( select anchor_data union [all] select recursive_part from R, ... ) select ...
As currently implemented by MariaDB and by the SQL Standard, data may be truncated if not correctly cast. It is necessary to 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. See the examples below.
Sample data:
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:
WITH RECURSIVE bus_dst as ( SELECT origin as dst FROM bus_routes WHERE origin='New York' UNION SELECT bus_routes.dst FROM bus_routes, bus_dst WHERE 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:
Next, the recursive part:
This time, we are trying to get bus routes such as “New York -> Washington -> Raleigh”.
Using the same sample data as the previous example:
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, bus_routes WHERE 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 | +-----------------------------+------------+
In the following example, data is truncated because the results are not specifically cast to a wide enough type:
WITH RECURSIVE tbl AS ( SELECT NULL AS col UNION SELECT "THIS NEVER SHOWS UP" AS col FROM tbl ) +------+ | col | +------+ | NULL | | | +------+
Explicitly use CAST to overcome this:
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 | +---------------------+
© 2019 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License.
https://mariadb.com/kb/en/recursive-common-table-expressions-overview/