SQL分解层次结构
从基本的员工/主管层次结构开始,我使用递归CTE构建级别: 与员工主管 像 挑选* 从…起 重视‘迈克’、‘丽莎’ “凯文”,“丽莎” “丽莎”,“肯” “肯”,“斯科特” “斯科特”,“克里斯” “克里斯”, RAWDATEMP,sup ,Hier 像 -锚位,无主管 选择1作为lvl ,emp ,sup 来自员工主管 其中sup= 联合所有 -递归成员 选择H.lvl+1作为lvl ,ES.emp ,ES.sup 来自雇员和主管 内连接层H 关于ES.sup=H.empSQL分解层次结构,sql,common-table-expression,hierarchical-data,recursive-query,Sql,Common Table Expression,Hierarchical Data,Recursive Query,从基本的员工/主管层次结构开始,我使用递归CTE构建级别: 与员工主管 像 挑选* 从…起 重视‘迈克’、‘丽莎’ “凯文”,“丽莎” “丽莎”,“肯” “肯”,“斯科特” “斯科特”,“克里斯” “克里斯”, RAWDATEMP,sup ,Hier 像 -锚位,无主管 选择1作为lvl ,emp ,sup 来自员工主管 其中sup= 联合所有 -递归成员 选择H.lvl+1作为lvl ,ES.emp ,ES.sup 来自雇员和主管 内连接层H 关于ES.sup=H.emp 在H.lvl+1中,
在H.lvl+1中,您需要修复递归查询,以便它正确地处理关系——同时,您希望跟踪原始员工 然后,最后一步是透视结果集。为此,可以在外部查询中使用条件聚合:
WITH hier AS (
SELECT 1 AS lvl
,emp
,sup
FROM EmployeeSupervisor
WHERE sup = ''
UNION ALL
SELECT H.lvl + 1 AS lvl
,H.emp
,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
您需要修复递归查询,以便它能够正确地处理关系——此外,您还希望跟踪原始员工 然后,最后一步是透视结果集。为此,可以在外部查询中使用条件聚合:
WITH hier AS (
SELECT 1 AS lvl
,emp
,sup
FROM EmployeeSupervisor
WHERE sup = ''
UNION ALL
SELECT H.lvl + 1 AS lvl
,H.emp
,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
事实上,你可以从不同的角度来看待它。 你需要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函数标记将路径拆分为列
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
, TOKEN(path,',',3) AS s3
, TOKEN(path,',',4) AS s4
, TOKEN(path,',',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
事实上,你可以从不同的角度来看待它。 你需要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函数标记将路径拆分为列
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
, TOKEN(path,',',3) AS s3
, TOKEN(path,',',4) AS s4
, TOKEN(path,',',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字段中创建层次结构。检查我添加的第三个字段 所以 然后,在最后一篇专栏文章中,你会看到像迈克、丽莎、肯、斯科特、克里斯这样的人物。 然后你需要分开。例如,使用此 这对你合适吗
PS:由于类型不匹配,我认为强制转换是必要的,但您可以将其更改为更高的数字。您可以保留所有以前的上级,并在csv字段中创建层次结构。检查我添加的第三个字段 所以 然后,在最后一篇专栏文章中,你会看到像迈克、丽莎、肯、斯科特、克里斯这样的人物。 然后你需要分开。例如,使用此 这对你合适吗
PS:我认为强制转换是必要的,因为类型不匹配,但您可以将其更改为更高的数字。尝试从员工级别向上递归。并在中间CTE上添加其他调试列,例如路径表达式:
WITH EmployeeSupervisor
AS (
SELECT cast(emp as varchar(20)) employee, cast(sup as varchar(20)) supervisor
FROM (
VALUES ('mike','lisa')
,('kevin','lisa')
,('lisa','ken')
,('ken','scott')
,('scott','chris')
,('chris',null)
) RawData(emp, sup)
)
, hier AS (
SELECT 1 AS lvl
,employee
,supervisor
, cast(concat('',employee,'->',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
, cast(concat('',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
尝试从员工级别向上递归。并在中间CTE上添加其他调试列,例如路径表达式:
WITH EmployeeSupervisor
AS (
SELECT cast(emp as varchar(20)) employee, cast(sup as varchar(20)) supervisor
FROM (
VALUES ('mike','lisa')
,('kevin','lisa')
,('lisa','ken')
,('ken','scott')
,('scott','chris')
,('chris',null)
) RawData(emp, sup)
)
, hier AS (
SELECT 1 AS lvl
,employee
,supervisor
, cast(concat('',employee,'->',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
, cast(concat('',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
如果您想要所有EMP的层次结构,那么必须从所有EMP开始,而不仅仅是根EMP。那么旋转就很简单了
WITH EmployeeSupervisor
AS (
SELECT *
FROM (
VALUES ('mike','lisa')
,('kevin','lisa')
,('lisa','ken')
,('ken','scott')
,('scott','chris')
,('chris','')
) RawData(emp, sup)
)
,Hier
AS (
-- all employees
SELECT 1 AS lvl
,emp
,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
请参见如果您想要所有EMP的层次结构,则必须从所有EMP开始,而不仅仅是根EMP。那么旋转就很简单了
WITH EmployeeSupervisor
AS (
SELECT *
FROM (
VALUES ('mike','lisa')
,('kevin','lisa')
,('lisa','ken')
,('ken','scott')
,('scott','chris')
,('chris','')
) RawData(emp, sup)
)
,Hier
AS (
-- all employees
SELECT 1 AS lvl
,emp
,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
请参见谢谢您的回复…我认为MAXCASE在。。。这种方法很好地解决了部分问题,但当我使用固定递归子查询运行这段代码时,它只返回最高级别的主管?谢谢你的回答…我想MAXCASE当。。。这种方法很好地解决了部分问题,但当我使用固定递归子查询运行这段代码时,它只返回最高级别的主管?谢谢你的回答。我同意这种方法,但最初的问题是嵌套的层次结构最多有20层,这是一个简单的例子。我知道我可以简单地再添加15个连接,但我希望有一个使用递归CTE的解决方案。如果没有使用递归CTEThanks为您的响应提供其他解决方案,我将接受这个答案。我同意这种方法,但最初的问题是嵌套的层次结构最多有20层,这是一个简单的例子。我知道我可以简单地再添加15个连接,但我希望有一个使用递归CTE的解决方案。如果没有使用递归CTE的其他解决方案,我会接受这个答案。您使用的是哪种引擎?@BartSchuijt SQL Server 2016您使用的是哪种引擎?@BartSchuijt SQL Server 2016 Well。。不,不完全是。看起来你希望最后一个专栏是迈克、丽莎、肯、斯科特、克里斯。如果是的话,我想这是可行的,但它实际上是作为lisa,ken输出的,其中emp='mike;'只有丽莎和肯?给迈克?嗯。。不,不完全是。看起来你希望最后一个专栏是迈克、丽莎、肯、斯科特、克里斯。如果是的话,我想这是可行的,但它实际上是作为lisa,ken输出的,其中emp='mike;'只有丽莎和肯?为了迈克?这让我有了90%的机会。谢谢代码所需的最后一个调整是添加
查询CTE后该员工的分组情况:按员工从上级分组中选择员工、maxsup1为sup1、maxsup2为sup2、maxsup3为sup3、maxsup4为sup4。这样,上面显示的输出就是实际输出。这让我得到了90%的结果。谢谢代码所需的最后一个调整是添加一个查询,该查询在CTE之后对员工进行分组:从Hier GROUP BY employee中选择employee、maxsup1作为sup1、maxsup2作为sup2、maxsup3作为sup3、maxsup4作为sup4。这样,上面显示的输出就是实际输出