Reputation: 830
I have a table with categories, each category as an ID, a Name and a ParentID. The problem is that there are 3 levels, parent categories, sub categories and child categories.
I can extract parent categories with a simple SELECT
and a WHERE ParentID IS NULL
clause as such:
SELECT *
FROM Category
WHERE ParentID IS NULL
However, a WHERE ParentID IS NOT NULL
clause will return both, sub categories as well as child categories.
I'm looking for a way to extract only sub categories, and only child categories.
Upvotes: 3
Views: 256
Reputation: 3758
How about something like:
-- Root parents
select c.* from categories c where c.ParentID is null
-- Second level. Select where parentid is a root category.
select c.* from categories c
where c.ParentID in (select c1.ID from categories c1 where c1.ParentID is null);
-- Third level. Select where parentid is a second level category
with second_level_cats (ID) as (
select c.ID from categories c
where c.ParentID in (select c1.ID from categories c1 where c1.ParentID is null)
)
select c.* from categories c
where c.ParentID in (select l2.ID from second_level_cats l2)
May not be entirely optimal but it seems to work. If there's only a relatively low number of rows and you're only ever going to go to three levels then it should suffice.
Upvotes: 1
Reputation: 79909
Typically, for these sort of problems, it is better to use the Recursive Queries Using Common Table Expressions. Something like so:
;WITH CategoriesTree(CategoryID, CategoryName, ParentName, CategoryLevel)
AS
(
SELECT
c.ID,
c.Name,
CAST('No Parent' AS VARCHAR(50)) AS ParentName,
0 AS CategoryLevel
FROM @Categories c
WHERE c.ParentID IS NULL
UNION ALL
SELECT c.ID, c.Name, p.CategoryName, p.CategoryLevel + 1
FROM CategoriesTree p
INNER JOIN @Categories c ON c.ParentID = p.CategoryID
)
SELECT *
FROM CategoriesTree
Where CategoryLevel = some id;
This will give you:
CATEGORYID CATEGORYNAME PARENTNAME CATEGORYLEVEL
1 Root Cateogry No Parent 0
2 Sub Cateogry 1 Root Cateogry 1
3 Sub Cateogry 2 Root Cateogry 1
4 Sub Cateogry 3 Root Cateogry 1
8 sub Cateogry 1 of 3 Sub Cateogry 3 2
7 Sub Cateogry 1 of 2 Sub Cateogry 2 2
5 Sub Cateogry 1 of 1 Sub Cateogry 1 2
6 sub Cateogry 2 of 1 Sub Cateogry 1 2
Using this query, you can control what level of categories you want to select. For instance, for the sample data, I used in the previous demo, here is the categories tree:
1: RootCategory Category Level: 0
|
|
----------------------------
| | |
| | |
2: Sub1 3: Sub2 4: sub3 Category Level: 1
| | |
------------ | |
| | | |
| | | |
5: Sub1of1 6: Sub2of1 7: sub1of2 8: sub1of3 Category Level: 2
This query will give you this categories tree with the new generated CategoryLevel
column.
Note that: In the sample data I used in the demo, there was only one parent category (the categories with parentid
IS NULL). However, the query will for work fine in case there were a lot of parent categories. And this because of the anchor query of the CTE, which is:
SELECT
c.ID,
c.Name,
CAST('No Parent' AS VARCHAR(50)) AS ParentName,
0 AS CategoryLevel
FROM @Categories c
WHERE c.ParentID IS NULL;
Then, you can use the generated column CategoryLevel
to select only the child categories of the level that you are interested in.
For example, if you need to select only the sub categories of the first sub categories of the root category, you can get these categories using the predicate CategoryLevel = 2
:
;WITH CategoriesTree(CategoryID, CategoryName, ParentName, CategoryLevel)
AS
(
...
)
SELECT *
FROM CategoriesTree
WHERE CategoryLevel = 2;
This will give you:
CATEGORYID CATEGORYNAME PARENTNAME CATEGORYLEVEL
8 sub Cateogry 1 of 3 Sub Cateogry 3 2
7 Sub Cateogry 1 of 2 Sub Cateogry 2 2
5 Sub Cateogry 1 of 1 Sub Cateogry 1 2
6 sub Cateogry 2 of 1 Sub Cateogry 1 2
Upvotes: 5
Reputation: 37633
First level categories - you have it:
SELECT *
FROM Category
WHERE ParentID IS NULL
For the second level categories you can try:
SELECT * FROM Category
WHERE ParentID IN (SELECT ID FROM Category WHERE ParentID IS NULL).
For the third:
SELECT * FROM Category
WHERE ParentID IN (SELECT ID FROM Category WHERE ParentID IS NOT NULL)
(Not tested)
Upvotes: 2