The code below is from: https://www.freecodecamp.org/news/mysql-common-table-expressions/
Data:
CREATE TABLE
categories (
id int,
cat_name varchar(100),
parent_category_id int DEFAULT NULL
);
INSERT INTO
categories
VALUES
(1, 'Mens', NULL),
(2, 'Tops', 1),
(3, 'Jerseys', 2),
(4, 'England', 3);
Code:
WITH RECURSIVE
category_tree AS (
SELECT
id,
cat_name,
parent_category_id,
cat_name AS full_name
FROM categories
WHERE parent_category_id IS NULL
UNION ALL
SELECT
c.id,
c.cat_name,
c.parent_category_id,
CONCAT (ct.full_name, ' > ', c.cat_name)
FROM categories c
JOIN category_tree ct
ON c.parent_category_id = ct.id
)
SELECT
id, full_name
FROM
category_tree;
categories:
| id | cat_name | parent_category_id |
| ---| ---------| -------------------|
| 1 | Mens | NULL |
| 2 | Tops | 1 |
| 3 | Jerseys | 2 |
| 4 | England | 3 |
category_tree:
| id | full_name |
| ---| ------------------------------- |
| 1 | Mens |
| 2 | Mens > Tops |
| 3 | Mens > Tops > Jerseys |
| 4 | Mens > Tops > Jerseys > England |
ON c.parent_category_id = ct.id
I would be grateful for any detailed explanations. Thank you very much.
See example. I added some additional data to your example to make the answer clearer.
Your data (source)
id | cat_name | parent_category_id |
---|---|---|
1 | Mens | null |
2 | Tops | 1 |
3 | Jerseys | 2 |
4 | England-1 | 3 |
5 | England-2 | 3 |
And recursive query
WITH RECURSIVE category_tree AS (
SELECT 0 as lvl, id, cat_name, parent_category_id,
cat_name AS full_name
FROM categories
WHERE parent_category_id IS NULL
UNION ALL
SELECT lvl+1 as lvl,c.id, c.cat_name,c.parent_category_id,
CONCAT (ct.full_name, ' > ', c.cat_name)
FROM category_tree ct
INNER JOIN categories c
ON c.parent_category_id = ct.id
)
SELECT *
FROM category_tree;
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
1 | 2 | Tops | 1 | Mens > Tops |
2 | 3 | Jerseys | 2 | Mens > Tops > Jerseys |
3 | 4 | England-1 | 3 | Mens > Tops > Jerseys > England-1 |
3 | 5 | England-2 | 3 | Mens > Tops > Jerseys > England-2 |
Let's look at the request step by step. I'll add column lvl for step numbering.
anchor
part of query SELECT 0 as lvl, id, cat_name, parent_category_id,
cat_name AS full_name
FROM categories
WHERE parent_category_id IS NULL
Output is
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
SELECT lvl+1 as lvl,c.id, c.cat_name,c.parent_category_id,
CONCAT (ct.full_name, ' > ', c.cat_name)
FROM category_tree ct
INNER JOIN categories c
ON c.parent_category_id = ct.id
This part output is
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
1 | 2 | Tops | 1 | Mens > Tops |
3.Union with anchor
result is
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
1 | 2 | Tops | 1 | Mens > Tops |
4.Next step - recursive part of query - JOIN step 3 result with source.
We JOIN (INNER)
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
1 | 2 | Tops | 1 | Mens > Tops |
with source table.
JOIN result is
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
2 | 3 | Jerseys | 2 | Mens > Tops > Jerseys |
And after UNION ALL
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
1 | 2 | Tops | 1 | Mens > Tops |
2 | 3 | Jerseys | 2 | Mens > Tops > Jerseys |
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
3 | 4 | England-1 | 3 | Mens > Tops > Jerseys > England-1 |
3 | 5 | England-2 | 3 | Mens > Tops > Jerseys > England-2 |
After UNION ALL
lvl | id | cat_name | parent_category_id | full_name |
---|---|---|---|---|
0 | 1 | Mens | null | Mens |
1 | 2 | Tops | 1 | Mens > Tops |
2 | 3 | Jerseys | 2 | Mens > Tops > Jerseys |
3 | 4 | England-1 | 3 | Mens > Tops > Jerseys > England-1 |
3 | 5 | England-2 | 3 | Mens > Tops > Jerseys > England-2 |