SQL故障分解层次结构

问题描述

从基本的员工/主管层次开始,我使用递归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


 

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...