Иерархический запрос - ограничение количества элементов на уровень - PullRequest
0 голосов
/ 22 ноября 2018

Я работаю с СУБД Oracle, и у меня возник вопрос относительно иерархических запросов.

Я создаю список организаций для своего отдела на работе с использованием иерархического запроса 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,Я смог получить самый близкий вывод к тому, чего я пытался достичь.Тем не менее, есть кое-что, что я хотел бы изменить в выводе, если это возможно.Я использую этот список для построения Org Chart в Oracle Apex, и с таблицей и запросом mathguy я получаю следующий вывод:

enter image description here

Этоочень близко к визуальному, который я пытаюсь произвести.Причиной «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:

enter image description here

Следующий его запрос:

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 следующий:

enter image description here

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Вот как я бы это сделал.

Сначала тестовые данные (я включил достаточно сотрудников, чтобы проиллюстрировать это, но не совсем 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
0 голосов
/ 22 ноября 2018

Примечание: это было отредактировано в значительной степени в ответ на обновление в вопросе и является неопределенным / не проверенным с апексом на совместимость

Приведенное ниже решение просто добавит менеджеров секцийза третьим уровнем 2. Но для уровня 3+, если не имеет значения, как назначаются дополнительные уровни помимо менеджеров секций, их можно назначить только на основе employee_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
...