用于从sql server中的查询获取以下输出的分层查询

时间:2014-05-27 12:20:44

标签: sql-server

我有一张叫做emp的桌子。该表有以下数据。

empid  name Manager_id
1       A   Null
2       B   1
3       C   2
4       D   2
5       E   4

我希望上表中的输出如下所示。

empid   name Manager_id Level1 Level2 Level3 Level4
1         A   null      A      null    null  null
2         B    1        A      B       null  null
3         C    2        A      B       C     null
4         D    2        A      B       D     D
5         E    4        A      B       D     E

2 个答案:

答案 0 :(得分:13)

with C as
(
  select T.EmpID,
         T.ManagerID,
         T.Name,
         cast('' as xml).query('element X { attribute V {sql:column("T.Name")}}') as LvlXML
  from YourTable as T
  where T.ManagerID is null
  union all
  select T.EmpID,
         T.ManagerID,
         T.Name,
         C.LvlXML.query('., element X { attribute V {sql:column("T.Name")}}')
  from YourTable as T
    inner join C 
      on T.ManagerID = C.EmpID
)
select C.EmpID,
       C.Name,
       C.ManagerID,
       C.LvlXML.value('/X[1]/@V', 'varchar(100)') as Level1,
       C.LvlXML.value('/X[2]/@V', 'varchar(100)') as Level2,
       C.LvlXML.value('/X[3]/@V', 'varchar(100)') as Level3,
       C.LvlXML.value('/X[4]/@V', 'varchar(100)') as Level4,
       C.LvlXML.value('/X[5]/@V', 'varchar(100)') as Level5
from C;

SQL Fiddle

<强>更新

@ t-clausen.dk指出上面的查询的性能不是它的原因所以这里是一个更快的版本。

首先在ManagerID上添加一个索引,其中Name为包含列。

create index IX_YourTable_ManagerID on YourTable(ManagerID) include(Name)

在我们进行递归时构建所需列的新查询。

with C as
(
  select T.EmpID,
         T.ManagerID,
         T.Name,
         T.Name as Level1,
         cast(null as varchar(100)) as Level2,
         cast(null as varchar(100)) as Level3,
         cast(null as varchar(100)) as Level4,
         1 as Lvl
  from YourTable as T
  where T.ManagerID is null
  union all
  select T.EmpID,
         T.ManagerID,
         T.Name,
         C.Level1,
         case when C.lvl = 1 then T.Name else C.Level2 end,
         case when C.lvl = 2 then T.Name else C.Level3 end,
         case when C.lvl = 3 then T.Name else C.Level4 end,
         C.Lvl + 1
  from YourTable as T
    inner join C 
      on T.ManagerID = C.EmpID
)
select C.EmpID,
       C.Name,
       C.ManagerID,
       C.Level1,
       C.Level2,
       C.Level3,
       C.Level4
from C;

这给了你这个漂亮的小查询计划,在查询的锚点和递归部分都有索引搜索:

enter image description here

SQL Fiddle

答案 1 :(得分:3)

我会使用PIVOT,我想这会提供最佳性能。

-- testtable and data
DECLARE @t table(Empid int identity(1,1),  Name char(1), Manager_id int)
INSERT @t VALUES('A',Null),('B',1),('C',2),('D',2),('E',4)

-- query
;WITH CTE as
(
  SELECT 
    Name,
    Manager_id,
    Name lvlName,
    Empid,
    1 reverselvl
  FROM @t
  UNION ALL
  SELECT 
    CTE.Name,
    T.Manager_id,
    T.Name lvlName,
    CTE.Empid,
    CTE.reverselvl + 1
  FROM @t T
  JOIN CTE
  ON T.Empid = CTE.Manager_id
), 
CTE2 as
(
  SELECT Empid, 
  count(*) over (partition by Empid) - reverselvl lvl,
  lvlName, 
  max(Manager_id) over (partition by Empid) Manager_id,
  CTE.Name
  FROM CTE
)
SELECT 
  Empid, 
  Name, 
  Manager_id, 
  [0] Level1, [1] Level2, [2] Level3, [3] Level4
FROM CTE2
PIVOT (max(lvlName) FOR [lvl] IN ([0],[1],[2],[3])) AS pvt 
OPTION (maxrecursion 0)

结果:

Empid Name Manager_id Level1 Level2 Level3 Level4
1     A    NULL       A      NULL   NULL   NULL
2     B    1          A      B      NULL   NULL
3     C    2          A      B      C      NULL
4     D    2          A      B      D      NULL
5     E    4          A      B      D      E