Рекурсивно агрегировать значения по родителям в Oracle
Рассмотрим следующую примерную структуру:
ОТДЕЛ
- Я БЫ
- PARENT_ID
- НАЗВАНИЕ
- ГЛУБИНА
ПРОЕКТ
- Я БЫ
- НАЗВАНИЕ
- СТОИМОСТЬ
- department_id
Некоторые данные, только ради примеров ниже:
| ID | PARENT_ID | NAME | DEPTH |
|----|-----------|-------|-------|
| 1 | NULL | DEPT1 | 1 |
| 2 | 1 | DEPT2 | 2 |
| 3 | 1 | DEPT3 | 2 |
| 4 | 2 | DEPT4 | 3 |
| 5 | 3 | DEPT5 | 3 |
| 6 | NULL | DEPT6 | 1 |
| 7 | 6 | DEPT7 | 2 |
| ID | NAME | COST | DEPARTMENT_ID |
|------|--------|-------|---------------|
| 1 | PRJ1 | 100 | 1 |
| 2 | PRJ2 | 200 | 2 |
| 3 | PRJ3 | 300 | 3 |
| 4 | PRJ4 | 400 | 4 |
| 5 | PRJ5 | 500 | 5 |
| 6 | PRJ6 | 600 | 6 |
| 7 | PRJ7 | 700 | 7 |
Теперь мне нужно как-то агрегировать затраты на проекты по одному отделу, а затем по непосредственным дочерним элементам.
Если выбран фильтр DEPT1, ожидаемый результат:
| LINE | DEPARTMENT_ID | PARENT_ID | NAME | AGGREGATE_COST |
|------|----------------|-----------|--------|----------------|
| 1 | 1 | NULL | DEPT1 | 1500 |
| 2 | 2 | 1 | DEPT2 | 600 |
| 3 | 3 | 1 | DEPT3 | 800 |
Куда:
- Совокупность строки 3: PRJ5 (от DEPT5, которая является дочерней по отношению к DEPT3) + PRJ3 (от DEPT3).
- Совокупность строки 2 - это PRJ4 (от DEPT4, которая является дочерней по отношению к DEPT2) + PRJ2 (от DEPT2).
- Строка 1 агрегат является суммой его детских агрегатов.
- Затраты на PRJ6 и PRJ7 игнорируются, поскольку они относятся к DEPT6 и DEPT7, и они не входят в иерархию DEPT1 (DEPT6 будет его братом, а не ребенком)
РЕДАКТИРОВАТЬ:
| ID | NAME | COST | DEPARTMENT_ID |
|------|--------|-------|---------------|
| 1 | PRJ1 | 1 | 1 |
| 2 | PRJ2 | 1 | 1 |
| 3 | PRJ3 | 1 | 2 |
| 4 | PRJ4 | 1 | 2 |
| 5 | PRJ5 | 1 | 4 |
В этом сценарии решение, представленное ivanzg, похоже, не работает. Я получаю удвоенные результаты для проектов в высших рангах
Если я получу агрегат для DEPT1, он возвращает что-то похожее на это:
| LINE | DEPARTMENT_ID | PARENT_ID | NAME | AGGREGATE_COST |
|------|----------------|-----------|--------|----------------|
| 1 | 1 | NULL | DEPT1 | 8 |
| 2 | 2 | NULL | DEPT1 | 4 |
1 ответ
Вы можете пометить строки в запросе иерархии (для последующего создания групп) с помощью оператора иерархии CONNECT_BY_ROOT. В запросе иерархии, делая все строки корневыми строками, вы создаете каждую комбинацию иерархии, в дальнейшем только указанные комбинации берутся и агрегируются. Для ваших тестовых данных это возвращает то, что вы указали.
SELECT ROOT_DEPT AS DEPARTMENT_ID
,ROOT_PARENT AS PARENT_ID
,ROOT_NAME AS NAME
,SUM(COST) AS AGGREGATE_COST
FROM (SELECT COST
,CONNECT_BY_ROOT DEPARTMENT_ID ROOT_DEPT
,CONNECT_BY_ROOT PARENT_ID ROOT_PARENT
,CONNECT_BY_ROOT NAME ROOT_NAME
FROM (SELECT B.DEPARTMENT_ID
,NVL(A.PARENT_ID,'0') PARENT_ID
,A.NAME
,SUM(B.COST) COST
FROM DEPARTMENT A
JOIN PROJECT B
ON A.ID = B.DEPARTMENT_ID
--> GROUP COST OF PROJECTS IN THE SAME DEPARTMENT IF THERE ARE ANY
GROUP BY B.DEPARTMENT_ID
,NVL(A.PARENT_ID,'0')
,A.NAME
)
--> MAKE ALL ROWS ROOT ROWS
CONNECT BY PRIOR DEPARTMENT_ID = PARENT_ID
)
WHERE ROOT_DEPT = 1 OR ROOT_PARENT = 1
GROUP BY ROOT_DEPT
,ROOT_PARENT
,ROOT_NAME