在表中排列层次结构

时间:2014-08-08 07:05:44

标签: sql sql-server sql-server-2008 tsql

我有一个包含以下数据的表dbo.Hierarchy:

  Level1 Level2 Level3 Level4 Level5 Level6 Level7 Level8 Level9 Level10
-----------------------------------------------------------------------
    a      b      c      d      e      f      g      h      i      j
    k      l      m      n      o      

总共有10个级别,任何项目都可以具有任何级别的层次结构。在上面的数据中,a是b的父级,b是c的父级,依此类推。 j和o是各自层次结构中的最后一个级别。如何以下列格式获得输出:

  Name   ParentName   LevelID
-------------------------------
   a         NULL        1
   b         a           2
   j         i           10
   k         NULL        1
   l         k           2
   o         n           5

2 个答案:

答案 0 :(得分:0)

试试这个:

;with base as
(select *, row_number() over (order by level1) rn from tbl),

 src as
(
 select 
 valname as name, 
 cast(substring(colname,6,len(colname)) as int) as level, 
 rn from
(select * from base) s
unpivot
(
 valname
 for colname in ([level1],[level2],[level3],[level4],[level5],[level6],[level7], [level8],[level9],[level10])
) u
),

cte as
(select * from src
 where level = 1
 union all
 select s.* from src s
 inner join cte c on s.level = c.level + 1 and s.rn = c.rn)

select distinct s.name, t.name parentname, s.level levelid from 
cte s
left join cte t on s.rn = t.rn and s.level = t.level + 1

故障:

  1. CTE base 用于生成行号作为派生列。我们将使用此列来跟踪哪些值属于哪一行。这将有助于将儿童独特地映射到他们的父母。
  2. CTE src 是我们将表从非规范化结构转换为规范化结构的地方。使用UNPIVOT,我们会将数据集减少到3列 - namelevel和行号rn
  3. CTE cte 是一种递归CTE,我们用它来获取父母和孩子的所有可能组合(包括直系父母和祖先)。
  4. 最后,我们LEFT JOIN cte 自身,条件是连接两侧的行号相同,即值属于基表中的相同记录,并且右侧的值是左侧值的直接祖先(父级)。
  5. Demo

    如果要为表选择规范化结构,可以避免上面的大量代码。我会建议这样的事情:

    CREATE TABLE tbl
    (ID int, --Keep track of values that are related to each other
     Name varchar(100), --Name
     Level int --The level for a particular value
    )
    

    使用这个提议的结构,您只需要递归CTE(来自上面代码的cte)和左连接来获取父子数据。这种方法的优点在于您可以将其扩展到您喜欢的任意数量的级别,而无需对级别编号进行硬编码。

    Demo

答案 1 :(得分:0)

像(未经测试的)

with t(L1,L2,L3,L4,L5,L6,L7,L8,L9,L10) as (
    values ('a','b','c','d','e','f','g','h','i','j')
         , ('k','l','m','n','o',null,null,null,null,null)
) 
select x.* 
from t 
cross apply ( 
   values (L1,null,1),(L2,L1,2),(L3,L2,3),(L4,L3,4),(L5,L4,5)
        , (L6,L5,6),(L7,L6,7),(L8,L7,8),(L9,L8,9),(L10,L9,10)) x (name, parentname, levelid) 
where name is not null