【问题标题】:Hierarchical query - limiting number of elements per level分层查询 - 限制每个级别的元素数量
【发布时间】:2018-11-21 23:39:02
【问题描述】:

我正在使用 Oracle DBMS,我有一个关于分层查询的问题。

我正在使用分层 SQL 查询为我的部门创建一个组织列表。组织结构分为三个级别,依次是部门经理、部门经理和团队成员。

到目前为止我的查询是:

SELECT level, employee_number, name, manager, department, phone
FROM employee_table
START WITH manager is null
CONNECT BY PRIOR employee_number = manager;

此查询生成一个层次结构列表,其中部门经理位于第一级,每个部门经理位于第二级,团队成员位于第三级。总的来说,只有一名部门经理,三名科室经理,30名团队成员。

问题:我的要求是将列表中每个级别的员工人数限制为最多三名员工。前两个级别没有问题,因为只有一个部门经理和三个科长(永远不会超过三个科长),但我目前在三级有30个团队成员(每个科长管理10个团队成员) )。我的目标是3级3个队员,4级3个队员,5级3个队员,以此类推。队员的顺序不重要,所以不管哪个队员在3 级,哪些团队成员处于 4 级等。

为了实现这个目标,我宁愿避免将团队成员的经理设置为另一个团队成员的员工编号。我可以在employee_table 中创建另一个名为“org_list_parent”的列,并指出一个团队成员的“org_list_parent”是另一个团队成员的employee_number,但如果可能的话,我更愿意避免这样做。

有人对这个问题有任何想法吗?

提前谢谢你。

更新:

使用 mathguy 的查询,我能够获得最接近我想要实现的输出。但是,如果可能的话,我想对输出进行一些调整。我正在使用此列表在 Oracle Apex 中构建组织结构图,并使用 mathguy 的表和查询,得到以下输出:

这与我试图制作的视觉效果非常接近。 “每级 3 名团队成员”背后的原因是为了防止图表在水平方向上过大。但是,如果您向左看,例如,员工 1104、1105 和 1106 在 1103 之下,员工 1107 在 1106 之下。最好是员工 1104 在 1101 之下,员工 1105 在 1102 之下,员工 1106 在 1103 之下,和员工 1107 在 1104 下。有什么方法可以编辑查询,以便直观地产生结果?

更新 2:

alexgibbs 要求就他提出的两个不同查询提供反馈,作为我的问题的解决方案。以下是他的第一个查询:

SELECT CASE WHEN LEVEL < 3
          THEN LEVEL
        ELSE 3 + FLOOR((DENSE_RANK() OVER (PARTITION BY MANAGER ORDER BY 
EMPLOYEE_NUMBER ASC ) - 1) / 3) END AS ADJUSTED_LEVEL,
EMPLOYEE_NUMBER, MANAGER
FROM EMPLOYEE_TABLE
START WITH MANAGER IS NULL
CONNECT BY PRIOR EMPLOYEE_NUMBER = MANAGER
ORDER BY 2 ASC, 1 ASC;

以下是此查询在 Oracle Apex 中的列表输出:

以下是他的第二个查询:

SELECT
CASE WHEN LEVEL < 3
 THEN LEVEL
 ELSE 3 + FLOOR((DENSE_RANK() OVER (PARTITION BY MANAGER ORDER BY 
EMPLOYEE_NUMBER ASC ) - 1) / 3) END AS ADJUSTED_LEVEL,
EMPLOYEE_NUMBER,
MANAGER,
DEPARTMENT
FROM EMPLOYEE_TABLE
START WITH MANAGER IS NULL
CONNECT BY PRIOR EMPLOYEE_NUMBER = MANAGER
ORDER BY
NVL2(MANAGER,1,0) ASC,
DEPARTMENT ASC,
CASE WHEN LEVEL < 3
 THEN LEVEL
 ELSE (MOD((DENSE_RANK() OVER (PARTITION BY MANAGER ORDER BY EMPLOYEE_NUMBER 
ASC )) - 1,3) + 3) END ASC,
ADJUSTED_LEVEL ASC;

以下是此查询在 Oracle Apex 中的输出:

【问题讨论】:

  • 一个“分层查询”,在没有LEVELSELECT 列表中其他此类计算的东西的情况下,只不过是行的排序,深度优先.你在乎那个命令吗?除此之外,在您的SELECT 列表中,您只有LEVEL - 这是您唯一需要更改的内容吗?另外:当您说每个级别只有 3 名员工时,您是指在所有行上,还是只有“每位经理的同一级别的 3 名员工”(所以每个级别总共有 9 名员工)?
  • 每个经理三个同级员工,所以每个级别总共有九个员工。我不完全理解你的前两个问题,但基本上我正在寻找的是 1 级的单个部门经理,2 级的 3 个部门经理(部门经理的顺序无关紧要),9 个团队成员级别 3(每个部门经理 3 个),级别 4 的 9 个团队成员等。无论哪个团队成员处于哪个级别(3、4 等),但级别 3 中团队成员的父级需要成为他们各自的第 2 级部门经理。
  • 谢谢凯瑟琳。如果添加第四个部门经理,您希望发生什么?
  • 这是一个很好的问题,@alexgibbs,虽然我不认为它会发生,但我想这是可能的。在这种情况下,我希望第四个部门经理与其他部门经理处于二级,然后在二级以下的每行中有 12 名团队成员(四名部门经理各有 3 名团队成员)。无论第 2 级的部门经理有多少,我总是希望 3、4、5 等级的每个部门经理有 3 名团队成员。
  • 好的,谢谢凯瑟琳。我会按照这些思路进行。

标签: sql oracle oracle11g hierarchy


【解决方案1】:

我会这样做。

首先是测试数据(我包括了足够多的员工来说明这一点,但每个中层管理人员的人数还不到 10 人)。由于与手头的问题无关,我省略了电话号码。

create table employee_table(employee_number, name, manager, department) as
    select 1001, 'Big Boss', null, 100 from dual union all
    select 1100, 'Beth Mgr', 1001, 100 from dual union all
    select 1101, 'Jim'     , 1100, 100 from dual union all
    select 1102, 'Jackie'  , 1100, 100 from dual union all
    select 1103, 'Helen'   , 1100, 100 from dual union all
    select 1104, 'Tom'     , 1100, 100 from dual union all
    select 1105, 'Vance'   , 1100, 100 from dual union all
    select 1106, 'Rosa'    , 1100, 100 from dual union all
    select 1107, 'Chuck'   , 1100, 100 from dual union all
    select 1200, 'Duck Mgr', 1001, 200 from dual union all
    select 1201, 'Danny'   , 1200, 200 from dual union all
    select 1202, 'Henry'   , 1200, 200 from dual union all
    select 1203, 'Mac'     , 1200, 200 from dual union all
    select 1204, 'Hassan'  , 1200, 200 from dual union all
    select 1205, 'Ann'     , 1200, 200 from dual union all
    select 1300, 'Adam Mgr', 1001, 300 from dual union all
    select 1301, 'Wendy'   , 1300, 300 from dual
;

然后是查询。我希望输出遵循“层次顺序”(如果我们不必弄乱级别的话)。为此,我首先运行分层查询,捕获ROWNUM 以对最终结果进行排序,然后修改外部查询中的级别。请注意,我使用LVL 作为列名; LEVEL 是保留字,所以不能用作列名。

select   case lvl when 3 then lvl + ceil(rn/3) - 1 else lvl end as lvl,
         employee_number, name, manager, department
from     (
          select     level as lvl, employee_number, name, manager, department,
                     rownum as ord, 
                     row_number() over 
                        (partition by manager order by employee_number) as rn
          from       employee_table
          start with manager is null
          connect by prior employee_number = manager
         )
order by ord
;

输出

       LVL EMPLOYEE_NUMBER NAME        MANAGER DEPARTMENT
---------- --------------- -------- ---------- ----------
         1            1001 Big Boss                   100
         2            1100 Beth Mgr       1001        100
         3            1101 Jim            1100        100
         3            1102 Jackie         1100        100
         3            1103 Helen          1100        100
         4            1104 Tom            1100        100
         4            1105 Vance          1100        100
         4            1106 Rosa           1100        100
         5            1107 Chuck          1100        100
         2            1200 Duck Mgr       1001        200
         3            1201 Danny          1200        200
         3            1202 Henry          1200        200
         3            1203 Mac            1200        200
         4            1204 Hassan         1200        200
         4            1205 Ann            1200        200
         2            1300 Adam Mgr       1001        300
         3            1301 Wendy          1300        300

【讨论】:

  • 您提出了一个很好的观点,但我还不确定哪个查询会产生我正在寻找的确切输出。今晚和明天我需要花一些时间用我的数据测试这两个查询。我感谢您为此所做的工作,在我测试您的查询后,我一定会以更明确的答复返回并发表评论。非常感谢您的帮助!
  • 是的,没问题 - 适应您的需求,如果您有后续问题,请回来。
  • 请看我在原始问题中的更新:)
【解决方案2】:

注意:已针对问题中的更新进行了重大编辑,并且不确定/未与 apex 进行兼容性测试

下面的解决方案只会在级别 2 的第三个之外添加部分经理。但是对于级别 3+,如果在部分经理之外如何分配其他级别并不重要,则可以根据 employee_id 分配它们。更新后,三人组的每个成员都应将其伪下一级直接嵌套在下面,下面是一个示例,它根据员工 ID 在三人组中分配伪级别和子级别,部门。

下面是一个示例数据集和查询。

CREATE TABLE EMPLOYEE_TABLE (
  EMPLOYEE_NUMBER NUMBER,
  MANAGER NUMBER DEFAULT NULL,
  NAME CHARACTER VARYING(64 BYTE),
  DEPARTMENT CHARACTER VARYING(64 BYTE),
  PHONE CHARACTER VARYING(64 BYTE)
);


--Dept Manager
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (10,NULL, 1);
--Section Managers
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (200,10, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (300,10, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (400,10, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (500,10, 5);
-- Section Employees
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2010,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2020,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2030,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2040,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2050,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2060,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2070,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2080,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2090,200, 2);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (2100,200, 2);

INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3010,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3020,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3030,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3040,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3050,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3060,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3070,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3080,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3090,300, 3);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (3100,300, 3);

INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4010,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4020,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4030,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4040,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4050,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4060,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4070,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4080,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4090,400, 4);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (4100,400, 4);


INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5010,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5020,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5030,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5040,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5050,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5060,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5070,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5080,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5090,500, 5);
INSERT INTO EMPLOYEE_TABLE(EMPLOYEE_NUMBER, MANAGER, DEPARTMENT) VALUES (5100,500, 5);
COMMIT;

已编辑 查询:

SELECT
CASE WHEN LEVEL < 3
     THEN LEVEL
     ELSE 3 + FLOOR((DENSE_RANK() OVER (PARTITION BY MANAGER ORDER BY EMPLOYEE_NUMBER ASC ) - 1) / 3) END AS ADJUSTED_LEVEL,
EMPLOYEE_NUMBER,
MANAGER,
DEPARTMENT
FROM EMPLOYEE_TABLE
START WITH MANAGER IS NULL
CONNECT BY PRIOR EMPLOYEE_NUMBER = MANAGER
ORDER BY
NVL2(MANAGER,1,0) ASC,
DEPARTMENT ASC,
CASE WHEN LEVEL < 3
     THEN LEVEL
     ELSE (MOD((DENSE_RANK() OVER (PARTITION BY MANAGER ORDER BY EMPLOYEE_NUMBER ASC )) - 1,3) + 3) END ASC,
ADJUSTED_LEVEL ASC;

结果:

  ADJUSTED_LEVEL   EMPLOYEE_NUMBER   MANAGER DEPARTMENT
               1                10           1
               2               200        10 2
               3              2010       200 2
               4              2040       200 2
               5              2070       200 2
               6              2100       200 2
               3              2020       200 2
               4              2050       200 2
               5              2080       200 2
               3              2030       200 2
               4              2060       200 2
               5              2090       200 2
               2               300        10 3
               3              3010       300 3
               4              3040       300 3
               5              3070       300 3
               6              3100       300 3
               3              3020       300 3
               4              3050       300 3
               5              3080       300 3
               3              3030       300 3
               4              3060       300 3
               5              3090       300 3
               2               400        10 4
               3              4010       400 4
               4              4040       400 4
               5              4070       400 4
               6              4100       400 4
               3              4020       400 4
               4              4050       400 4
               5              4080       400 4
               3              4030       400 4
               4              4060       400 4
               5              4090       400 4
               2               500        10 5
               3              5010       500 5
               4              5040       500 5
               5              5070       500 5
               6              5100       500 5
               3              5020       500 5
               4              5050       500 5
               5              5080       500 5
               3              5030       500 5
               4              5060       500 5
               5              5090       500 5

【讨论】:

  • 非常感谢您提供非常详细和快速的解决方案,@alexgibbs。今晚和明天我将花时间用我的数据集测试您的查询,以确保它产生我正在寻找的确切输出,然后我将对您的解决方案做出更明确的回应来发表评论。非常感谢您的帮助!
  • 谢谢凯瑟琳。我会回应@mathguy - 如果有任何跟进会有所帮助,请告诉我。同时,我相信 mathguy 的解决方案具有更好、更简洁的布局和自然的排序;我会推荐这个解决方案。
  • 请看我对原始问题的更新:)
  • 谢谢凯瑟琳。我不使用 apex,所以我不能在这里做出自信的更新。我将添加一个临时编辑,它只是重新分配和重新排序,但提前道歉它可能不是你想要的。我也会很大程度上推迟到@mathguy,因为您的更新使用了来自 mathguy 答案的数据。尽管如此,我现在会更新一个最好的猜测。
  • 好的@KatherineReed 更新了。如果这是否符合您正在寻找的布局,我将不胜感激。谢谢!
猜你喜欢
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
相关资源
最近更新 更多