分层查询 - 限制每个级别的元素数量

Hierarchical query - limiting number of elements per level

我正在使用 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 级有三个团队成员,在第 4 级有三个团队成员,在第 5 级有三个团队成员,等等。团队成员的顺序无关紧要,所以哪个团队成员在3 级,哪些团队成员处于 4 级,等等

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

有人对这个问题有什么想法吗?

提前谢谢你。

更新:

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

这与我要制作的视觉效果非常接近。 “每个级别 3 个团队成员”背后的原因是试图防止图表在水平方向上过大。但是,如果你向左看,比如员工1104、1105、1106在1103下面,员工1107在1106下面。最好员工1104在1101下面,员工1105在1102下面,员工1106在1103下面,员工 1107 位于 1104 下方。是否有任何方法可以编辑查询以直观地生成该结果?

更新二:

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 中的输出:

注意:已针对问题的更新进行了大量编辑,uncertain/not-tested 具有兼容性的顶点

下面的解决方案只会在第 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

下面是我的做法。

首先是测试数据(我包括了足够的员工来说明这一点,但不是每个中层经理有 10 名员工)。我遗漏了 phone 数字,因为它与手头的问题无关。

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
;

然后查询。我希望输出遵循 "hierarchical order"(如果我们不必弄乱级别,就会这样)。为此,我首先 运行 层次查询,捕获 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