I have Categories stored in a single table.
Where there is no limit on number of childerns.
I want to fetch all the linked childern categories for the provided category id:
The reason for getting the hierarchy is that I need to update the path field for each category that is either newly created or updated. I need to maintain the path field
Table name: categories
id parentId name path
A1 null Cat 1 Cat 1
A2 A1 Cat 2 Cat 1 > Cat 2
A3 A2 Cat 3 Cat 1 > Cat 2 > Cat 3
A4 null Cat A Cat A
A5 A4 Cat B Cat A > Cat B
Now I want to fetch hierarchy for id: 1
What I have tried so far is:
with recursive cte (id, name, parentId) AS (
select
id,
name,
parentId
from
categories
where
parentId = 'A1'
union
all
select
c.id,
c.name,
c.parentId
from
categories c
inner join cte on c.parentId = cte.id
)
select
*
from
cte;
The above query returns:
[
{
id: A1,
parentId: null,
name: Cat 1,
path: Cat 1
},
{
id: A2,
parentId: A1,
name: Cat 2,
path: Cat 1 > Cat 2
}
]
But I want this:
[
{
id: A2,
parentId: A1,
name: Cat 2,
path: Cat 1 > Cat 2
},
{
id: A3,
parentId: A2,
name: Cat 3,
path: Cat 1 > Cat 2 > Cat 3
}
]
If I provide id: 2
, in that case I am expecting:
[
{
id: A3,
parentId: A2,
name: Cat 3,
path: Cat 1 > Cat 2 > Cat 3
}
]
There is something that I am doing wrong with the query, can anyone identify?
Here is reproduced scenario: https://dbfiddle.uk/Beefs-UH
IMPORTANT NOTE:
The primary key i.e id is a unique identifier string not an integer. So the records cannot be sorted on id.
3
Answers
You left out the root node from your result set by applying a filter on the "parentId" column. (Your root has no parent.)
Here’s an aproach that works:
(Cat 3’s parent is Cat 2, Cat 2’s parent is Cat 1, and Cat 1 has no parent: root is reached.)
But since you want to maintain the "path" field, it might be easier to calculate all paths by using a recursive CTE and then check for mismatch:
Solution for your after-edit problem:
https://dbfiddle.uk/agO_kNXf
To get the up-hierarchy try the following:
Here, we order the results by the generated ‘ord’ value, you don’t need to order by the id.
To get the down-hierarchy, use the following:
To get the full hierachy (up and down), you can union the results of the both recursive queries as the following:
Since you are storing the full path for each id, you can try another approach using self-join as the following:
For up-hierarchy:
Here, we used the
length(path)
to order the results, this will guarantee that the most upper parent appears first and the lowest child appears last.For down-hierarchy, use
on C1.path like CONCAT('%', C2.name, '%')
.For full hierarchy, use
on C1.path like CONCAT('%', C2.name, '%') or C2.path like CONCAT('%', C1.name, '%')
.See a demo on MySQL.
See a demo on SQL Server (with minor modifications).
The data in
categories
table represents a tree structure connected by nodes (category):Step 1. Create the table schema (with slight modification)
Step 2. Generate test data and each node could have zero to three children nodes (Please feel free to modify CTE to include more
union all
s.My test data looks like this (YMMV because of RAND() function):
The tree can be visualized as below:
Step 3. Retrieve all linked parent nodes and children nodes (ie. ID=12)
Result: