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
答案 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;
<强>更新强>
@ 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;
这给了你这个漂亮的小查询计划,在查询的锚点和递归部分都有索引搜索:
答案 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