Получить MASTER родитель для каждого элемента - PullRequest
0 голосов
/ 20 ноября 2018

У меня есть простой запрос:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION
from DEPARTMENT dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

query

Это возвращает информацию о departments с некоторыми строками, имеющими одинаковые DEP_ID,DEP_NAME и PARENT_DEP_NAME.Это ожидаемый результат, поскольку некоторые сотрудники принадлежат к одному и тому же отделу, но имеют другой SITE_LOCATION

. Мне бы хотелось добавить еще один столбец MASTER_PARENT_ID с DEP_ID родительского элемента MASTER.

То, что я называю главным родителем, - это то, на которое ссылаются как PARENT_DEP_ID, но на самом деле не существует (отсутствует значение в столбце DEP_ID).

Таким образом, все эти строки должны фактическииметь значение MASTER_PARENT_ID равное DEP_2000.Это только примерные данные, но на самом деле многие строки имеют различный MASTER_PARENT_ID.Не все из них имеют одинаковое значение.

Чтобы сделать это, для каждой строки мне нужно выполнить рекурсивный запрос, чтобы пройти весь путь по дереву, пока я не найду значение PARENT_DEP_ID, которое не 'у меня нет совпадений DEP_ID.

Я пытаюсь прочитать и понять документацию и примеры Oracle, но не могу найти, что работает в моем случае.Должен ли я использовать что-то вроде CONNECT BY PRIOR для выполнения такой рекурсивной функции?

SQL вообще не моя чашка чая и даже меньше Oracle.Я не могу найти, как решить эту проблему.

Спасибо

Ответы [ 2 ]

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

Вы можете использовать псевдостолбец CONNECT_BY_ISLEAF, чтобы найти листья дерева иерархии, а затем использовать функцию CONNECT_BY_ROOT( ... ), чтобы получить значения при начале навигации по дереву:

SQL Fiddle

Настройка схемы Oracle 11g R2 :

CREATE TABLE DEPARTMENT( DEP_ID, DEP_NAME, PARENT_DEP_ID ) AS
SELECT 'DEP_2000', 'Dep0', NULL       FROM DUAL UNION ALL
SELECT 'DEP_2400', 'Dep1', 'DEP_2000' FROM DUAL UNION ALL
SELECT 'DEP_2410', 'Dep2', 'DEP_2400' FROM DUAL UNION ALL
SELECT 'DEP_2420', 'Dep3', 'DEP_2400' FROM DUAL;

Запрос 1 :

SELECT CONNECT_BY_ROOT( DEP_ID )        AS DEP_ID,
       CONNECT_BY_ROOT( DEP_NAME )      AS DEP_NAME,
       CONNECT_BY_ROOT( PARENT_DEP_ID ) AS PARENT_DEP_ID,
       DEP_ID                           AS MASTER_PARENT_DEP_ID
FROM   DEPARTMENT
WHERE  CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR PARENT_DEP_ID = DEP_ID

Результаты :

|   DEP_ID | DEP_NAME | PARENT_DEP_ID | MASTER_PARENT_DEP_ID |
|----------|----------|---------------|----------------------|
| DEP_2000 |     Dep0 |        (null) |             DEP_2000 |
| DEP_2400 |     Dep1 |      DEP_2000 |             DEP_2000 |
| DEP_2410 |     Dep2 |      DEP_2400 |             DEP_2000 |
| DEP_2420 |     Dep3 |      DEP_2400 |             DEP_2000 |

Примечание. При обходе дерева от каждого элемента до корня, а не в обратном порядке, вам не требуется ни выражение START WITH, ниотдельный запрос для поиска корней.

Это будет работать, даже если нет строки DEP_2000 (просто измените DEP_ID на PARENT_DEP_ID):

SQL Fiddle

Настройка схемы Oracle 11g R2 :

CREATE TABLE DEPARTMENT( DEP_ID, DEP_NAME, PARENT_DEP_ID ) AS
--SELECT 'DEP_2000', 'Dep0', NULL       FROM DUAL UNION ALL
SELECT 'DEP_2400', 'Dep1', 'DEP_2000' FROM DUAL UNION ALL
SELECT 'DEP_2410', 'Dep2', 'DEP_2400' FROM DUAL UNION ALL
SELECT 'DEP_2420', 'Dep3', 'DEP_2400' FROM DUAL;

Запрос 1 :

SELECT CONNECT_BY_ROOT( DEP_ID )        AS DEP_ID,
       CONNECT_BY_ROOT( DEP_NAME )      AS DEP_NAME,
       CONNECT_BY_ROOT( PARENT_DEP_ID ) AS PARENT_DEP_ID,
       PARENT_DEP_ID                    AS MASTER_PARENT_DEP_ID
FROM   DEPARTMENT
WHERE  CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR PARENT_DEP_ID = DEP_ID

Результаты :

|   DEP_ID | DEP_NAME | PARENT_DEP_ID | MASTER_PARENT_DEP_ID |
|----------|----------|---------------|----------------------|
| DEP_2400 |     Dep1 |      DEP_2000 |             DEP_2000 |
| DEP_2410 |     Dep2 |      DEP_2400 |             DEP_2000 |
| DEP_2420 |     Dep3 |      DEP_2400 |             DEP_2000 |
0 голосов
/ 20 ноября 2018

Если идентификатор главного отдела вообще не существует в таблице отделов - вместо того, чтобы просто не отображаться в результатах, например, из-за отсутствия непосредственных сотрудников - тогда вы можете использовать иерархический запрос для получения корневого родителядля каждой выгрузки:

select dep_id, dep_name, parent_dep_id,
  connect_by_root(parent_dep_id) as master_parent_id
from department
connect by parent_dep_id = prior dep_id
start with parent_dep_id in (
  select parent_dep_id from department d1
  where not exists (
    select *
    from department d2
    where d2.dep_id = d1.parent_dep_id
  )
);

DEP_ID   DEP_NAME        PARENT_D MASTER_P
-------- --------------- -------- --------
DEP_2400 Department 2400 DEP_2000 DEP_2000
DEP_2410 Department 2410 DEP_2400 DEP_2000
DEP_2420 Department 2420 DEP_2400 DEP_2000

, а затем использовать это в качестве встроенного представления в вашем основном запросе вместо прямой ссылки на таблицу:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION,
dep.MASTER_PARENT_ID
from (
  select dep_id, dep_name, parent_dep_id,
    connect_by_root(parent_dep_id) as master_parent_id
  from department
  connect by parent_dep_id = prior dep_id
  start with parent_dep_id in (
    select parent_dep_id from department d1
    where not exists (
      select *
      from department d2
      where d2.dep_id = d1.parent_dep_id
    )
  )
) dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

DEP_ID   DEP_NAME        PARENT_D SITE_LO MASTER_P
-------- --------------- -------- ------- --------
DEP_2400 Department 2400 DEP_2000 SITE_01 DEP_2000
DEP_2400 Department 2400 DEP_2000 SITE_02 DEP_2000
DEP_2410 Department 2410 DEP_2400 SITE_01 DEP_2000
DEP_2410 Department 2410 DEP_2400 SITE_02 DEP_2000
DEP_2420 Department 2420 DEP_2400 SITE_01 DEP_2000
DEP_2420 Department 2420 DEP_2400 SITE_02 DEP_2000

Это выглядело бы более естественным для DEP_2000 чтобы действительно существовать, хотя и для него не иметь родителя;если это действительно так, то встроенное представление проще:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION,
dep.MASTER_PARENT_ID
from (
  select dep_id, dep_name, parent_dep_id,
    connect_by_root(dep_id) as master_parent_id
  from department
  connect by parent_dep_id = prior dep_id
  start with parent_dep_id is null
) dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

Обратите внимание, что наряду с предложением start with, ищущим нулевого родителя, connect_by_root() теперь смотрит на dep_id вместо parent_dep_id (что будет нулевым).

...