棘手的选择声明

时间:2012-12-06 07:31:00

标签: sql sql-server tsql select

我有一个包含类别的表,每个类别都有一个ID,一个名称和一个ParentID。问题是有3个级别,父类别,子类别和子类别。

我可以使用简单的SELECTWHERE ParentID IS NULL子句提取父类别:

SELECT *
FROM Category
WHERE ParentID IS NULL

但是,WHERE ParentID IS NOT NULL子句将同时返回子类别和子类别。

我正在寻找一种方法来只提取子类别,而只提取子类别。

3 个答案:

答案 0 :(得分:5)

通常,对于这类问题,最好使用Recursive Queries Using Common Table Expressions。像这样:

;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;

SQL Fiddle Demo

这会给你:

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

这是如何工作的?

使用此查询,您可以控制要选择的类别级别。例如,对于我在上一个演示中使用的示例数据,这里是类别树:

                        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

此查询将为您提供包含新生成的CategoryLevel列的此类别树。

请注意:在我在演示中使用的示例数据中,只有一个父类别(parentid的类别为IS NULL)。但是,如果有很多父类别,查询将工作正常。这是因为CTE的锚点查询,即:

SELECT 
     c.ID, 
     c.Name, 
     CAST('No Parent' AS VARCHAR(50)) AS ParentName, 
     0 AS CategoryLevel
FROM @Categories c
WHERE c.ParentID IS NULL;

然后,您可以使用生成的列CategoryLevel仅选择您感兴趣的级别的子类别。

例如,如果您只需要选择根类别的第一个子类别的子类别,则可以使用谓词CategoryLevel = 2获取这些类别:

;WITH CategoriesTree(CategoryID, CategoryName, ParentName, CategoryLevel)
AS
(
    ...
)
SELECT * 
FROM CategoriesTree
WHERE CategoryLevel = 2;

这会给你:

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

SQL Fiddle Demo

答案 1 :(得分:2)

第一级别类别 - 你拥有它:

SELECT *
FROM Category
WHERE ParentID IS NULL

对于第二级别类别,您可以尝试:

SELECT * FROM Category
WHERE ParentID IN (SELECT ID FROM Category WHERE ParentID IS NULL).

第三个:

SELECT * FROM Category
WHERE ParentID IN (SELECT ID FROM Category WHERE ParentID IS NOT NULL)

(未经测试)

答案 2 :(得分:1)

如下:

-- 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)

可能不是完全最佳但似乎有效。如果只有相对较少的行数,而你只需要达到三个级别那么就足够了。