问题描述
从基本的员工/主管层次开始,我使用递归CTE建立级别:
WITH EmployeeSupervisor
AS (
SELECT *
FROM (
VALUES ('mike','lisa'),('kevin',('lisa','ken'),('ken','scott'),('scott','chris'),('chris','')
) RawData(emp,sup)
),Hier
AS (
-- anchor level,no supervisor
SELECT 1 AS lvl,emp,sup
FROM EmployeeSupervisor
WHERE sup = ''
UNION ALL
-- recursive member
SELECT H.lvl + 1 AS lvl,ES.emp,ES.sup
FROM EmployeeSupervisor ES
INNER JOIN Hier H
ON ES.sup = H.emp
WHERE H.lvl + 1 <= 5 -- max of 5 levels
AND ES.sup != ''
)
SELECT *
FROM Hier
我尝试在带有COALESCE的PIVOT上进行一些变体,以获得所需的输出(如下查询所示),但没有成功。
-- expected output
SELECT *
FROM (
VALUES ('mike','lisa','ken','scott','chris',NULL),NULL,NULL)
) Expected(lvl1,lvl2,lvl3,lvl4,lvl5)
那里有很多类似的问题,但是没有一个能真正解决这个问题的本质。
- 编辑:使用SQL Server 2016,并希望避免使用有利于或递归CTE的众多重复连接。
解决方法
实际上,您可以以不同的方式看待它。 您希望并排5个级别,将其硬接线到5个级别。 您可能会像建立硬连线的5向左自连接一样构建它...
WITH
rawdata(ord,emp,sup) AS ( -- adding an order integer,to keep the order
SELECT 1,'mike','lisa'
UNION ALL SELECT 2,'kevin','lisa'
UNION ALL SELECT 3,'lisa','ken'
UNION ALL SELECT 4,'ken','scott'
UNION ALL SELECT 5,'scott','chris'
UNION ALL SELECT 6,'chris',''
)
SELECT
l0.emp,l1.emp,l2.emp,l3.emp,l4.emp
FROM rawdata l0
LEFT JOIN rawdata l1 ON l0.sup=l1.emp
LEFT JOIN rawdata l2 ON l1.sup=l2.emp
LEFT JOIN rawdata l3 ON l2.sup=l3.emp
LEFT JOIN rawdata l4 ON l3.sup=l4.emp
ORDER BY l0.ord
;
-- out emp | emp | emp | emp | emp
-- out -------+--------+--------+--------+-------
-- out mike | lisa | ken | scott | chris
-- out kevin | lisa | ken | scott | chris
-- out lisa | ken | scott | chris | (null)
-- out ken | scott | chris | (null) | (null)
-- out scott | chris | (null) | (null) | (null)
-- out chris | (null) | (null) | (null) | (null)
下一次尝试将路径字符串连接起来,然后使用SQL Server函数TOKEN()
将路径拆分为列...
WITH RECURSIVE r AS (
SELECT
1 AS lvl,emp AS path,*
FROM rawdata
WHERE sup=''
UNION ALL
SELECT
p.lvl + 1,p.path + ',' + c.emp AS path,c.*
FROM rawdata c
JOIN r AS p
ON c.sup = p.emp
)
SELECT
TOKEN(path,',1) AS s1,TOKEN(path,2) AS s2,3) AS s3,4) AS s4,5) AS s5
FROM r;
-- s1 | s2 | s3 | s4 | s5
-- -------+-------+-----+------+-------
-- chris | | | |
-- chris | scott | | |
-- chris | scott | ken | |
-- chris | scott | ken | lisa |
-- chris | scott | ken | lisa | mike
-- chris | scott | ken | lisa | kevin
,
您可以保留所有先前的上级主管,并在csv字段中创建层次结构。 (检查我添加的第三个字段) 所以
WITH EmployeeSupervisor
AS (
SELECT *
FROM (
VALUES ('mike','lisa'),('kevin',('lisa','ken'),('ken','scott'),('scott','chris'),('chris','','')
) RawData(emp,sup,hierarchy)
),Hier
AS (
-- anchor level,no supervisor
SELECT 1 AS lvl,cast (emp as varchar(255)) hierarchy
FROM EmployeeSupervisor
WHERE sup = ''
UNION ALL
-- recursive member
SELECT H.lvl + 1 AS lvl,ES.emp,ES.sup,cast(ES.emp+','+H.hierarchy as varchar(255))
FROM EmployeeSupervisor ES
INNER JOIN Hier H
ON ES.sup = H.emp
WHERE H.lvl + 1 <= 5 -- max of 5 levels
AND ES.sup != ''
)
SELECT *
FROM Hier
然后,在最后一列中,您将得到类似mike,lisa,ken,scott,chris
的内容。
然后您需要拆分。例如,使用此answer
您可以吗?
PS:由于类型不匹配,我认为强制转换是必需的,但是您可以将其更改为更大的数字。
,尝试从员工级别重新开始。并将其他调试列放在中间CTE上,例如“ path”表达式:
WITH EmployeeSupervisor
AS (
SELECT cast(emp as varchar(20)) employee,cast(sup as varchar(20)) supervisor
FROM (
VALUES ('mike',null)
) RawData(emp,sup)
),hier AS (
SELECT 1 AS lvl,employee,supervisor,cast(concat('','->',supervisor) as varchar(max)) path,cast(supervisor as varchar(20)) sup1,cast(null as varchar(20)) sup2,cast(null as varchar(20)) sup3,cast(null as varchar(20)) sup4,cast(null as varchar(20)) sup5
FROM EmployeeSupervisor
UNION ALL
SELECT H.lvl + 1 AS lvl,H.employee employee,es2.employee,h.path,es.supervisor) as varchar(max)) path,null sup1,case when H.lvl + 1 = 2 then cast(es2.employee as varchar(20)) end sup2,case when H.lvl + 1 = 3 then cast(es2.employee as varchar(20)) end sup3,case when H.lvl + 1 = 4 then cast(es2.employee as varchar(20)) end sup4,case when H.lvl + 1 = 5 then cast(es2.employee as varchar(20)) end sup5
FROM Hier H
join EmployeeSupervisor es
ON H.supervisor = ES.employee
join EmployeeSupervisor es2
ON es.supervisor = es2.employee
WHERE H.lvl + 1 <= 5
)
SELECT
employee,MAX(sup1) sup1,MAX(sup2) sup2,MAX(sup3) sup3,MAX(sup4) sup4,MAX(sup5) sup5
FROM Hier
GROUP BY employee
输出:
employee sup1 sup2 sup3 sup4 sup5
-------------------- -------------------- -------------------- -------------------- -------------------- --------------------
chris NULL NULL NULL NULL NULL
ken scott chris NULL NULL NULL
kevin lisa ken scott chris NULL
lisa ken scott chris NULL NULL
mike lisa ken scott chris NULL
scott chris NULL NULL NULL NULL
,
如果要所有emp的层次结构,则必须以所有emp开头,而不仅仅是根。然后旋转很简单。
WITH EmployeeSupervisor
AS (
SELECT *
FROM (
VALUES ('mike',Hier
AS (
-- all employees
SELECT 1 AS lvl,sup
FROM EmployeeSupervisor
UNION ALL
-- recursive supervisors
SELECT H.lvl + 1 AS lvl,H.emp,ES.sup
FROM EmployeeSupervisor ES
JOIN Hier H
ON ES.emp = H.sup
WHERE H.lvl < 5 -- max of 5 levels
AND ES.sup <> ''
)
SELECT *
FROM Hier
pivot (max(sup)
for lvl in ([1],[2],[3],[4],[5])
) as pvt
请参见fiddle
,您需要修复递归查询,以便其正确处理关系-此外,您还希望跟踪原始员工。
然后,是枢纽结果集的最后一步。为此,您可以在外部查询中使用条件聚合:
WITH hier AS (
SELECT 1 AS lvl,sup
FROM EmployeeSupervisor
WHERE sup = ''
UNION ALL
SELECT H.lvl + 1 AS lvl,ES.sup
FROM EmployeeSupervisor ES
INNER JOIN Hier H
ON H.sup = ES.emp
WHERE H.lvl + 1 <= 5
)
SELECT
emp,MAX(CASE WHEN lvl = 1 THEN sup END) sup1,MAX(CASE WHEN lvl = 2 THEN sup END) sup2,MAX(CASE WHEN lvl = 3 THEN sup END) sup3,MAX(CASE WHEN lvl = 4 THEN sup END) sup4,MAX(CASE WHEN lvl = 5 THEN sup END) sup5
FROM Hier
GROUP BY emp