Reputation: 4608
I have self referenced table - HIERARCHY(id, name, parent_id)
.
So I need to get all the hierarchy by any node of this hierarchy. For example we have tree, where h1
, h2
are roots:
-(h1)
| |_(h1_1)
| | |_(h1_1_2)
| |_(h1_2)
| |_(h1_2_1)
-(h2)
| |_(h2_1)
| |_(h2_2)
|
What I need, it to get all the tree e.g with root h1
by any node of this tree e.g. by h1_2
-(h1)
|_(h1_1)
get | |_(h1_1_2) by h1_2 or h1_2_1, etc
|_(h1_2)
|_(h1_2_1)
I wrote the query:
WITH RECURSIVE hierarchy_with_parents(id) AS (
SELECT l.id, l.name, l.parent_id FROM hierarchy AS l WHERE l.id = <any_row_id>
UNION ALL
SELECT lc.id, lc.name, lc.parent_id FROM hierarchy lc, hierarchy_with_parents lwp WHERE lc.id = lwp.parent_id
), hierarchy_with_children(id) AS (
SELECT l.id, l.name, l.parent_id FROM hierarchy AS l WHERE l.id
IN ( -- sub-query for getting parent id
SELECT
lwp.id
FROM hierarchy_with_parents AS lwp
WHERE lwp.parent_id IS NULL
)
UNION ALL
SELECT lc.id, lc.name, lc.parent_id FROM hierarchy lc, hierarchy_with_children lwc WHERE lc.parent_id = lwc.id
)
SELECT * FROM hierarchy_with_children
hierarchy_with_parents
- returns subtree from child to parent(inclusive),
hierarchy_with_children
- returns all tree.
It seems all works Ok, but I am not DB-expert and I want to know limitations and comments about my query. Also any other solutions for PostgreSQL and Oracle 11g welcome.
Thanks.
Upvotes: 0
Views: 887
Reputation:
Given :input_node
, first find the root of the subtree containing that node. This is done in the find_root
factored subquery. Then simply collect all the rows that link to that root. I need the NVL()
call in the outer query in case the :input_node
was a root already; in that case find_root
returns no rows. When used as a scalar subquery, that is treated as null
(at least in Oracle), so I can use NVL()
to fix it.
with
hierarchy as (
select 2 as child, 1 as parent from dual union all
select 3, 2 from dual union all
select 4, 1 from dual union all
select 5, 4 from dual union all
select 7, 6 from dual union all
select 8, 7 from dual union all
select 9, 6 from dual
),
find_root as (
select parent as rt
from hierarchy
where connect_by_isleaf = 1
start with child = :input_node
connect by child = prior parent
)
select child, parent
from hierarchy
start with parent = nvl((select rt from find_root), :input_node)
connect by parent = prior child;
Upvotes: 1