返回层次结构而不创建任何视图或表?

时间:2011-09-05 01:40:58

标签: sql-server-2008

我有一个包含列和数据的表格如下:

Table1
ID  Name    PID
A1  Apple   P1
B1  Book    A1
B2  Brook   A1
C1  Cat     B1
C2  Cook    B1
C3  Car     B1
D1  Dog     B2
D2  Doll    B2
E1  Egg     C1

我希望结果如下:

ID  Name                    Depth
B1  Apple\Book              2
C1  Apple\Book\Cat          3
E1  Apple\Book\Cat\Egg      4
C2  Apple\Book\Cook         3
C3  Apple\Book\Car          3
B2  Apple\Brook             2
D1  Apple\Brook\Dog         3
D2  Apple\Brook\Doll        3

如果该行的PID等于父行的ID,则该行是另一行的子项。

Apple是基地。所以第一个陈述就像是:

Select ID, Name, 2 from Table1 where PID=(select ID from Table1 where Name='Apple');

我目前的解决方案是创建大量视图并将所有类似的语句保存到视图中并将它们组合在一起。但我不希望这样。我想在1个select语句中完成它.A

3 个答案:

答案 0 :(得分:2)

我不知道是否有一种优雅的方式来获得您列出的确切顺序,但这是一种带有递归CTE的方法:

;WITH cte AS
(
    SELECT ID, Name, Depth = 1
        FROM dbo.Table1 
        WHERE Name = 'Apple'
    UNION ALL
    SELECT t.ID, t.Name, Depth = cte.Depth + 1
        FROM cte 
        INNER JOIN dbo.Table1 AS t
        ON t.PID = cte.ID
)
SELECT ID, Name, Depth
FROM cte
WHERE Depth > 1;

答案 1 :(得分:1)

declare @Table1 table
(
  ID varchar(2),
  Name varchar(10),
  PID varchar(2)
)

insert into @Table1 values  
('A1',  'Apple',   'P1'),
('B1',  'Book',    'A1'),
('B2',  'Brook',   'A1'),
('C1',  'Cat',     'B1'),
('C2',  'Cook',    'B1'),
('C3',  'Car',     'B1'),
('D1',  'Dog',     'B2'),
('D2',  'Doll',    'B2'),
('E1',  'Egg',     'C1')

;with C as
(
  select T.ID,
         cast(T.Name as varchar(max)) as Name,
         1 as Depth
  from @Table1 as T
  where T.Name = 'Apple'
  union all
  select T.ID,
         cast(C.Name+'\'+T.Name as varchar(max)),
         C.Depth + 1
  from @Table1 as T
    inner join C
      on T.PID = C.ID
)
select C.ID,
       C.Name,
       C.Depth
from C
where C.Depth > 1
order by C.Name

编辑没有Apple。

;with C as
(
  select T.ID,
         cast(T.Name as varchar(max)) as Name,
         1 as Depth
  from @Table1 as T
    inner join @Table1 as TP
      on T.PID = TP.ID
  where TP.Name = 'Apple'
  union all
  select T.ID,
         cast(C.Name+'\'+T.Name as varchar(max)),
         C.Depth + 1
  from @Table1 as T
    inner join C
      on T.PID = C.ID
)
select C.ID,
       C.Name,
       C.Depth
from C
order by C.Name

答案 2 :(得分:0)

这是我在PostgreSQL上提出的查询。我没有SQL Server,所以我无法测试,但谷歌让我相信这个查询结构也适用于SQL Server。

SELECT t1.*, (
    WITH q AS (
        SELECT t2.*
        FROM Table1 AS t2
        WHERE t2.ID = t1.ID
           OR (t1.ID IS NULL AND t2.ID IS NULL)

        UNION ALL

        SELECT t3.*
        FROM Table1 AS t3
        JOIN q
        ON t3.ID = q.PID
        AND t3.ID <> 'P1'
        -- 'P1' on the above line is the "sentinel" value you want to
        -- stop traversing at.  Remove the AND clause altogether if you
        -- want to traverse up to the ultimate root record.
    )
    SELECT COUNT(q.ID)
    FROM q
) AS depth
FROM Table1 AS t1;

这是在PostgreSQL上运行的示例。请注意,PostgreSQL在声明引用自身的公用表表达式时需要WITH RECURSIVE。这和示例数据是两个查询之间的唯一区别:

$ WITH Table1(ID, Name, PID) AS (VALUES
$     ('A1', 'Apple', 'P1'),
$     ('B1', 'Book',  'A1'),
$     ('B2', 'Brook', 'A1'),
$     ('C1', 'Cat',   'B1'),
$     ('C2', 'Cook',  'B1'),
$     ('C3', 'Car',   'B1'),
$     ('D1', 'Dog',   'B2'),
$     ('D2', 'Doll',  'B2'),
$     ('E1', 'Egg',   'C1')
$ )
$ SELECT t1.*, (
$     WITH RECURSIVE q AS (
$         SELECT t2.*
$         FROM Table1 AS t2
$         WHERE t2.ID = t1.ID
$            OR (t1.ID IS NULL AND t2.ID IS NULL)
$
$         UNION ALL
$
$         SELECT t3.*
$         FROM Table1 AS t3
$         JOIN q
$         ON t3.ID = q.PID
$         AND t3.ID <> 'P1'
$     )
$     SELECT COUNT(q.ID)
$     FROM q
$ ) AS depth
$ FROM Table1 AS t1;
 id | name  | pid | depth
----+-------+-----+-------
 A1 | Apple | P1  |     1
 B1 | Book  | A1  |     2
 B2 | Brook | A1  |     2
 C1 | Cat   | B1  |     3
 C2 | Cook  | B1  |     3
 C3 | Car   | B1  |     3
 D1 | Dog   | B2  |     3
 D2 | Doll  | B2  |     3
 E1 | Egg   | C1  |     4
(9 rows)