Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL分解层次结构_Sql_Common Table Expression_Hierarchical Data_Recursive Query - Fatal编程技术网

SQL分解层次结构

SQL分解层次结构,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中,

从基本的员工/主管层次结构开始,我使用递归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中,您需要修复递归查询,以便它正确地处理关系——同时,您希望跟踪原始员工

然后,最后一步是透视结果集。为此,可以在外部查询中使用条件聚合:

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。这样,上面显示的输出就是实际输出