Это получит полную нефильтрованную иерархию (при условии, что у менеджера верхнего уровня есть значение NULL
для managerid
):
SELECT *
FROM table_name
START WITH MANAGERID IS NULL
CONNECT BY PRIOR EMPID = MANAGERID
Затем вы можете отфильтровать, чтобы увидеть, есть ли активный статус в иерархии от каждого сотрудника до менеджера верхнего уровня:
SELECT *
FROM table_name t
WHERE EXISTS (
SELECT 1
FROM table_name
WHERE status IN (1,2,3)
START WITH EMPID = t.EMPID
CONNECT BY EMPID = PRIOR MANAGERID
)
START WITH MANAGERID IS NULL
CONNECT BY PRIOR EMPID = MANAGERID
Какой для тестовых данных:
CREATE TABLE table_name ( EMPNAME, EMPID, MANAGERID, STATUS ) AS
SELECT 'CEO', 1, NULL, 0 FROM DUAL UNION ALL
SELECT 'Mgr1', 2, 1, 1 FROM DUAL UNION ALL
SELECT 'Mgr2', 3, 1, 0 FROM DUAL UNION ALL
SELECT 'Mgr3', 4, 1, 0 FROM DUAL UNION ALL
SELECT 'SubMgr1.1', 5, 2, 0 FROM DUAL UNION ALL
SELECT 'SubMgr1.2', 6, 2, 1 FROM DUAL UNION ALL
SELECT 'SubMgr2.1', 7, 3, 0 FROM DUAL UNION ALL
SELECT 'SubMgr2.2', 8, 3, 1 FROM DUAL UNION ALL
SELECT 'SubMgr3.1', 9, 4, 0 FROM DUAL UNION ALL
SELECT 'Emp1.1.1', 10, 5, 0 FROM DUAL UNION ALL
SELECT 'Emp1.1.2', 11, 5, 1 FROM DUAL UNION ALL
SELECT 'Emp1.2.1', 12, 6, 0 FROM DUAL UNION ALL
SELECT 'Emp1.2.2', 13, 6, 1 FROM DUAL UNION ALL
SELECT 'Emp2.1.1', 14, 7, 0 FROM DUAL UNION ALL
SELECT 'Emp2.1.2', 15, 7, 1 FROM DUAL UNION ALL
SELECT 'Emp2.2.1', 16, 8, 0 FROM DUAL UNION ALL
SELECT 'Emp2.2.2', 17, 8, 1 FROM DUAL UNION ALL
SELECT 'Emp3.1.1', 18, 9, 0 FROM DUAL UNION ALL
SELECT 'Emp3.1.2', 19, 9, 1 FROM DUAL;
Дает вывод:
EMPNAME | EMPID | MANAGERID | STATUS
:-------- | ----: | --------: | -----:
Mgr1 | 2 | 1 | 1
SubMgr1.1 | 5 | 2 | 0
Emp1.1.1 | 10 | 5 | 0
Emp1.1.2 | 11 | 5 | 1
SubMgr1.2 | 6 | 2 | 1
Emp1.2.1 | 12 | 6 | 0
Emp1.2.2 | 13 | 6 | 1
Emp2.1.2 | 15 | 7 | 1
SubMgr2.2 | 8 | 3 | 1
Emp2.2.1 | 16 | 8 | 0
Emp2.2.2 | 17 | 8 | 1
Emp3.1.2 | 19 | 9 | 1
Чтобы проверить в обоих направлениях, просто спуститься по иерархии и подняться по ней:
SELECT *
FROM table_name t
WHERE EXISTS (
SELECT 1
FROM table_name
WHERE status IN (1,2,3)
START WITH EMPID = t.MANAGERID
CONNECT BY EMPID = PRIOR MANAGERID
UNION ALL
SELECT 1
FROM table_name
WHERE status IN (1,2,3)
START WITH EMPID = t.EMPID
CONNECT BY PRIOR EMPID = MANAGERID
)
START WITH MANAGERID IS NULL
CONNECT BY PRIOR EMPID = MANAGERID
Выходы:
EMPNAME | EMPID | MANAGERID | STATUS
:-------- | ----: | --------: | -----:
CEO | 1 | <em>null</em> | 0
Mgr1 | 2 | 1 | 1
SubMgr1.1 | 5 | 2 | 0
Emp1.1.1 | 10 | 5 | 0
Emp1.1.2 | 11 | 5 | 1
SubMgr1.2 | 6 | 2 | 1
Emp1.2.1 | 12 | 6 | 0
Emp1.2.2 | 13 | 6 | 1
Mgr2 | 3 | 1 | 0
SubMgr2.1 | 7 | 3 | 0
Emp2.1.2 | 15 | 7 | 1
SubMgr2.2 | 8 | 3 | 1
Emp2.2.1 | 16 | 8 | 0
Emp2.2.2 | 17 | 8 | 1
Mgr3 | 4 | 1 | 0
SubMgr3.1 | 9 | 4 | 0
Emp3.1.2 | 19 | 9 | 1
db <> fiddle здесь
Вы также можете использовать рекурсивное условие подзапроса-факторинга для распространения активного состояния при переходе по иерархии; вам все равно нужно проверить оставшуюся иерархию в другом направлении, если она не активна. Профилируйте оба решения и посмотрите, является ли одно более производительным, чем другое, поскольку это не то, что мы можем определить.
WITH data ( empname, empid, managerid, status, is_active ) AS (
SELECT t.empname,
t.empid,
t.managerid,
t.status,
CASE WHEN status IN (1,2,3) THEN 1 ELSE 0 END
FROM table_name t
WHERE managerid IS NULL
UNION ALL
SELECT t.empname,
t.empid,
t.managerid,
t.status,
CASE WHEN d.is_active = 1 OR t.status IN (1,2,3) THEN 1 ELSE 0 END
FROM data d
INNER JOIN table_name t
ON ( d.empid = t.managerid )
)
SELECT empname,
empid,
managerid,
status
FROM data d
WHERE is_active = 1
OR EXISTS (
SELECT 1
FROM table_name t
WHERE t.status IN ( 1, 2, 3)
START WITH d.empid = t.managerid
CONNECT BY PRIOR empid = managerid
)
db <> fiddle