Иерархический запрос Oracle с несколькими узлами - PullRequest
0 голосов
/ 04 июля 2018

У меня есть представление и таблица со следующими данными:

V_Mgmt

DMId    PMId    TLId
1       1       1
1       1       2
1       2       3
2       3       4
2       3       5
2       4       6

T_ProjLevels

TLId    DevId   ParentDevId
1       1       0
1       2       1
1       3       1
2       4       0
2       5       4
2       6       4
2       7       6
3       8       0
3       9       0
4       10      0
4       11      0
4       12      11

В идеале, моя древовидная структура была бы такой, как на левом изображении. Но я должен создать древовидную структуру согласно правильному изображению, пропуская TL.

enter image description here

До сих пор я успешно смог создать свое дерево только с DevId, используя следующий запрос. Нужна помощь в создании этой новой древовидной структуры.

SELECT DevId,ParentDevId from T_ProjLevels
START WITH ParentDevId=0
Connect By Nocycle  Prior "DevId" = "ParentDevId"
ORDER SIBLINGS BY ParentDevId

1 Ответ

0 голосов
/ 05 июля 2018

Посмотрите на свою структуру данных. У вас есть одна строка, представляющая DMID 1, PMID 1, которую вы хотите отобразить в виде двух отдельных строк в своем дереве. Каким-то образом вам нужно создать дополнительные строки в вашем наборе результатов для представления этих промежуточных узлов. Хороший способ сделать это - использовать GROUPING SETS.

Итак, мы будем объединять две таблицы и GROUP BY GROUPING SETS(...) таким образом, чтобы получить объединенную иерархию всех узлов, будь то узлы DM, PM или DEV.

Получив это, мы просто сделаем простой запрос CONNECT BY в объединенной иерархии.

Ниже я приведу работающую реализацию этого плана, но сначала позвольте мне сказать, что если вам часто приходится делать такие вещи, серьезно подумайте о том, что, возможно, ваша модель данных не разработана должным образом.

Вот запрос с комментариями. Эта версия даст вам дерево справа от вашего поста, то есть того, которое пропускает уровни TL. Простая модификация дала бы дерево слева вместо этого ... было неясно, какой именно вам нужен (извините).

-- First provide data to simulate your V_Mgmt table...
With V_Mgmt ( DMid, PMId, TLId ) AS (
SELECT 1,       1,       1 FROM DUAL UNION ALL
SELECT 1,       1,       2 FROM DUAL UNION ALL
SELECT 1,       2,       3 FROM DUAL UNION ALL
SELECT 2,       3,       4 FROM DUAL UNION ALL
SELECT 2,       3,       5 FROM DUAL UNION ALL
SELECT 2,       4,       6 FROM DUAL ),
-- ... and your T_ProjLevels table
T_ProjLevels (TLId,    DevId,  ParentDevId) AS (
SELECT 1,       1,       0 FROM DUAL UNION ALL
SELECT 1,       2,       1 FROM DUAL UNION ALL
SELECT 1,       3,       1 FROM DUAL UNION ALL
SELECT 2,       4,       0 FROM DUAL UNION ALL
SELECT 2,       5,       4 FROM DUAL UNION ALL
SELECT 2,       6,       4 FROM DUAL UNION ALL
SELECT 2,       7,       6 FROM DUAL UNION ALL
SELECT 3,       8,       0 FROM DUAL UNION ALL
SELECT 3,       9,       0 FROM DUAL UNION ALL
SELECT 4,       10,      0 FROM DUAL UNION ALL
SELECT 4,       11,      0 FROM DUAL UNION ALL
SELECT 4,       12,     11 FROM DUAL ),
-- Next, merge them together 
--  (A) using GROUPING_SETS to create extra rows for the DM, PM, but not the TL-level nodes
--  (B) combining DM, PM, (but not TL), and DEV ids into a common set of "node", "parent_node", and "node_name" columns
merged_hierarchy ( node, parent_node, node_name ) AS (
SELECT rtrim(m.dmid || '.' || m.pmid || '.' || pl.devid,'.') node, 
rtrim(
case 
when grouping(m.pmid) = 1 then NULL
when grouping(m.tlid) = 1 then to_char(m.dmid)
when grouping(pl.devid) = 1 then m.dmid || '.' || m.pmid
else
m.dmid || '.' || m.pmid || '.' || nullif(pl.parentdevid,0) end,'.') parent_node,
case when grouping(pl.devid) = 0 THEN 'DEV' || pl.devid
when grouping(m.tlid) = 0 THEN 'TL' || m.tlid
when grouping(m.pmid) = 0 THEN 'PM' || m.pmid
when grouping(m.dmid) = 0 THEN 'DM' || m.dmid
end node_name
from v_mgmt m 
left join t_projlevels pl on pl.tlid = m.tlid
group by grouping sets ( ( m.dmid ), ( m.dmid, m.pmid), (m.dmid, m.pmid, m.tlid, pl.devid, pl.parentdevid ) )
)
-- Finally, query the merged hierarchy as a straight-forward CONNECT BY query
SELECT lpad(' ',5*(level-1),' ') || node_name output
FROM merged_hierarchy
START WITH parent_node IS NULL
CONNECT BY parent_node = prior node 
-- Exclude the outer-joined rows from T_ProjLevels...
AND node_name != 'DEV';
+--------------------------+
|          OUTPUT          |
+--------------------------+
| DM1                      |
|      PM1                 |
|           DEV1           |
|                DEV2      |
|                DEV3      |
|           DEV4           |
|                DEV5      |
|                DEV6      |
|                     DEV7 |
|      PM2                 |
|           DEV8           |
|           DEV9           |
| DM2                      |
|      PM3                 |
|           DEV10          |
|           DEV11          |
|                DEV12     |
|      PM4                 |
+--------------------------+
...