1

假设我有表是这样的:层次结构,新列,非规范化

id parent_id name 
11  NULL  Company 
33  11  Department 1 
44  33  Department 2 

我想将其改造成:

id parent_id name   Level1  Level2  Level3 
11  NULL  Company  NULL  NULL   NULL 
22  11   Company Department 1 NULL   NULL 
33  22   Company Department 1 Department 2 NULL 

我能创造一个CTE并拿出级别列显示层次结构中的值,但我不知道如何为部门创建新列。

with myCTE as (
     select c.id, c."name", c.parent_id, 1 as Level 
     from table1 c 
     where c.parent_id IS NULL 

     UNION ALL 

     Select c1.id, c1."name", c1.parent_id, Level +1 
     from table1 c1 
     inner join myCTE on c1.parent_id = myCTE.id 
     where c1.parent_id IS NOT NULL 
    ) 

select * from myCTE 

显示:

  id  parent_id name   level 
    1  11  NULL  Company  1 
    2  22  11   Department 2 2 
    3  33  22   Department 3 3 
+0

最大深度是否受限制?你期望有多少个关卡? – Shnugo

+0

我期望它是理想的动态,但我已经达到了第9级 – user3015289

回答

2

一个(几乎)完全通用的方法:

DECLARE @tbl TABLE(id INT,parent_id INT,name VARCHAR(100)); 
INSERT INTO @tbl VALUES 
(11,NULL,'Company') 
,(33,11,'Department 1') 
,(44,33,'Department 2a') 
,(55,33,'Department 2b') 
,(66,44,'SubDep 2a'); 

--The递归CTE将建立在一个XML片段的行接一行水平 --The SELECT将使用XML方法.nodes()ROW_NUMBER以产生列名称为PIVOT

WITH recCTE AS 
(
    SELECT id, parent_id,name,(SELECT name AS [*] FOR XML PATH('')) AS NameConcat 
    FROM @tbl WHERE parent_id IS NULL 

    UNION ALL 

    SELECT t.id,t.parent_id,t.name,recCTE.NameConcat + '</lvl><lvl>' + (SELECT t.name AS [*] FOR XML PATH('')) 
    FROM @tbl AS t 
    INNER JOIN recCTE ON recCTE.id=t.parent_id 
) 
SELECT p.* 
FROM 
(
    SELECT id 
      ,parent_id 
      ,name 
      ,'Level' + REPLACE(STR(ROW_NUMBER() OVER(PARTITION BY id ORDER BY (SELECT NULL)),2),' ','0') AS HierarchyRank 
      ,lvl.value(N'(./text())[1]','nvarchar(max)') AS HierarchyName 
    FROM recCTE 
    CROSS APPLY (SELECT CAST('<lvl>' + NameConcat + '</lvl>' AS XML) AS PreLevels) AS Casted 
    CROSS APPLY Casted.PreLevels.nodes(N'/lvl') AS A(lvl) 
) AS tbl 
PIVOT 
(
    MAX(HierarchyName) FOR HierarchyRank IN(Level01,Level02,Level03,Level04,Level05,Level06,Level07,Level08,Level09) 
) AS p; 

结果

+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| id | parent_id | name   | Level01 | Level02  | Level03  | Level04 | Level05 | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| 11 | NULL  | Company  | Company | NULL   | NULL   | NULL  | NULL | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| 33 | 11  | Department 1 | Company | Department 1 | NULL   | NULL  | NULL | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| 44 | 33  | Department 2a | Company | Department 1 | Department 2a | NULL  | NULL | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| 55 | 33  | Department 2b | Company | Department 1 | Department 2b | NULL  | NULL | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 
| 66 | 44  | SubDep 2a  | Company | Department 1 | Department 2a | SubDep 2a | NULL | 
+----+-----------+----------------+---------+----------------+---------------+-----------+---------+ 

如果你需要更多的水平,唯一需要的是更多的列名添加到PIVOT part ...

+0

美丽的解决方案。 –

0

你可以计算的行,每一个级别,并团结他们:

with MyCTE as (

select id, parent_id, name, null as level1, null as level2, null as level3 
from table1 as root 
where root.parent_id is null 

union 

select level1.id, level1.parent_id, root.name, level1.name as level1, null as level2, null as level3 
from table1 as level1 
    inner join table1 as root on root.id = level1.parent_id 
where root.parent_id is null 

union 

select level2.id, level2.parent_id, root.name, level1.name as level1, level2.name as level2, null as level3 
from table1 as level2 
    inner join table1 as level1 on level1.id = level2.parent_id 
    inner join table1 as root on root.id = level1.parent_id 
where root.parent_id is null 

union 

select level3.id, level3.parent_id, root.name, level1.name as level1, level2.name as level2, level3.name as level3 
from table1 as level3 
    inner join table1 as level2 on level2.id = level3.parent_id 
    inner join table1 as level1 on level1.id = level2.parent_id 
    inner join table1 as root on root.id = level1.parent_id 
where root.parent_id is null 

) 
select * from MyCTE 

如果您需要更多的水平,则需要增加更多的选择与其他连接