I have a parent-child relationship in an Oracle 9i database-table
like:
parent | child
1 | 2
2 | 3
2 | 4
null | 1
1 | 8
I need to get the absolute parent from a given child.
Say, I have child 4, it has to give me parent: 1
I already looked to CONNECT BY , but I can't find the solution.
you could use a CONNECT BY query to build the list of parents and then filter :
SQL> WITH tree AS (
2 SELECT 1 parent_id, 2 child_id FROM DUAL
3 UNION ALL SELECT 2 , 3 FROM DUAL
4 UNION ALL SELECT 2 , 4 FROM DUAL
5 UNION ALL SELECT null, 1 FROM DUAL
6 UNION ALL SELECT 1 , 8 FROM DUAL
7 )
8 SELECT child_id
9 FROM (SELECT *
10 FROM tree
11 CONNECT BY PRIOR parent_id = child_id
12 START WITH child_id = 4)
13 WHERE parent_id IS NULL;
CHILD_ID
----------
1
SELECT parent
FROM (
SELECT parent
FROM (
SELECT parent, level AS l
FROM mytable
START WITH
child = 4
CONNECT BY
child = PRIOR parent
)
ORDER BY
l DESC
)
WHERE rownum = 1
This will give you NULL as the absolute parent.
If you want 1, replace parent with child:
SELECT child
FROM (
SELECT child
FROM (
SELECT child, level AS l
FROM mytable
START WITH
child = 4
CONNECT BY
child = PRIOR parent
)
ORDER BY
l DESC
)
WHERE rownum = 1
Related
My Oracle table looks like this
ID | ParentID
-----------------
1 | 0
2 | 1
3 | 2
4 | 3
5 | 3
If I know only ID and need to get all parent elements in oracle, what is the query I need to use?
ex:- If I pass 5, need to get 5 > 3 > 2 > 1
For example:
SQL> with test (id, parent) as
2 (select 1, 0 from dual union
3 select 2, 1 from dual union
4 select 3, 2 from dual union
5 select 4, 3 from dual union
6 select 5, 3 from dual
7 )
8 select listagg(id, '->') within group (order by level) result
9 from test
10 start with id = &par_id
11 connect by prior parent = id;
Enter value for par_id: 5
RESULT
---------------------------------------------------------------------
5->3->2->1
SQL>
You may use a recursive CTE
WITH cte (id, parentid, p)
AS (SELECT id,
parentid,
To_char(id) AS p
FROM t
WHERE id = :p_id --enter 5
UNION ALL
SELECT t.id,
t.parentid,
c.p
|| '>'
|| t.id AS p
FROM t
JOIN cte c
ON ( c.parentid = t.id ))
SELECT p
FROM cte
WHERE parentid = 0 --Highest parent.
Demo
I'm working on a query which use connect by prior.
I have written a query which retrieves all children of an entity. What I want is to retrieve both children and parents rows.
Here is my SQL :
Select *
From myTable tab
Connect By Prior tab.id= tab.child_id
Start With tab.id= 2;
How could I retrieve parents ?
Thanks.
Use UNION ALL to combine a query to get the children with another to get the ancestors.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE myTable ( id, child_id ) AS
SELECT 0, 1 FROM DUAL UNION ALL
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 3 FROM DUAL UNION ALL
SELECT 3, 4 FROM DUAL UNION ALL
SELECT 4, 5 FROM DUAL UNION ALL
SELECT 3, 6 FROM DUAL UNION ALL
SELECT 0, 7 FROM DUAL UNION ALL
SELECT 1, 8 FROM DUAL;
Query 1:
SELECT * -- Child Rows
FROM mytable
START WITH id = 2
CONNECT BY PRIOR child_id = id
UNION ALL
SELECT * -- Ancestor Rows
FROM mytable
START WITH child_id = 2
CONNECT BY PRIOR id = child_id
Results:
| ID | CHILD_ID |
|----|----------|
| 2 | 3 |
| 3 | 4 |
| 4 | 5 |
| 3 | 6 |
| 1 | 2 |
| 0 | 1 |
An alternative approach to a hierarchical query, if you're on 11gR2 or higher, is recursive subquery factoring:
with rcte (id, child_id, some_other_col) as (
select id, child_id, some_other_col -- whichever columns you're interested in
from myTable
where id = 2
union all
select t.id, t.child_id, t.some_other_col -- whichever columns you're interested in
from rcte r
join myTable t
on t.id = r.child_id -- match parents
or t.child_id = r.id -- match children
)
cycle id set is_cycle to 1 default 0
select id, child_id, some_other_col -- whichever columns you're interested in
from rcte
where is_cycle = 0;
The anchor member finds your initial target row. The recursive member then looks for parents or children of each row found so far.
The final query can get whichever columns you want, do aggregation, etc.
(Possibly worth testing both approaches with real data to see if there is a performance difference, of course).
Say I have table A:
id child_id
1 2
2 3
3 NULL
4 NULL
5 6
6 7
7 8
8 NULL
I need a query to get root parent id of each row, this output for example:
id root_parent_id
1 1
2 1
3 1
4 4
5 5
6 5
7 5
8 5
I've tried both CONNECT BY and CTE by examples, but seems all I found based on rows with parent_id and not child_id and doesn't work.
CONNECT BY query I tried:
SELECT id, child_id, LEVEL, connect_by_root id
FROM a
CONNECT BY id = PRIOR child_id
CTE query I tried:
WITH recursion_view (base,
id,
child_id)
AS (
SELECT id base, id, child_id FROM a
UNION ALL
SELECT
previous_level.base,
current_level.id,
current_level.child_id
FROM recursion_view previous_level, a current_level
WHERE current_level.id = previous_level.child_id)
SELECT base,
id,
child_id
FROM recursion_view
ORDER BY base, id, child_id
You need to traverse the hierarchy in reverse order - view the root parents as leaves. Something like this:
with table_a ( id, child_id ) as (
select 1, 2 from dual union all
select 2, 3 from dual union all
select 3, NULL from dual union all
select 4, NULL from dual union all
select 5, 6 from dual union all
select 6, 7 from dual union all
select 7, 8 from dual union all
select 8, NULL from dual
)
select connect_by_root id as id, id as root_parent_id
from table_a
where connect_by_isleaf = 1
connect by child_id = prior id
order by id
;
ID ROOT_PARENT_ID
-- --------------
1 1
2 1
3 1
4 4
5 5
6 5
7 5
8 5
I have a table name tree and it have two columns one is p and second one is ch.
p ch
-------------
1 2
1 3
1 4
2 5
2 6
7 8
9 10
11 12
6 13
13 14
14 15
14 16
Output I want is all the linked child to a parent. If I give "1" as input for which I have to find all parent and child elements for example , in this case 1 is parent for 2,3,4 and 2 is parent for 5 and so on...in this case I need every linked child element and parent itself as mentioned below:
ParentChilds
------------
1
2
3
4
5
6
13
14
15
16
Below is the query I wrote and I want to confirm it is the best possible solution or we can do it better way, because i have large data in my table :
with LinkedAccounts (p, ch, orig_recur, dest_recur, lvl) as (
select n.p
, n.ch
, 1 orig_recur
, case n.p
when 1 then ch
else p
end dest_recur
, 1 lvl
from tree n
where n.p = 1
or n.ch = 1 union all
select n.p
, n.ch
, LinkedAccounts.dest_recur orig_recur
, case n.p
when LinkedAccounts.dest_recur then n.ch
else n.p
end dest_recur
, LinkedAccounts.lvl + 1 lvl
from LinkedAccounts
join tree n
on (n.p = LinkedAccounts.dest_recur and n.ch != LinkedAccounts.orig_recur)
or (n.ch = LinkedAccounts.dest_recur and n.p != LinkedAccounts.orig_recur)
)
search breadth first by orig_recur, dest_recur set ordering
cycle ch,
p set is_cycle to '1' default '0' select distinct p from LinkedAccounts union Select Distinct ch from LinkedAccounts;
Use a hierarchical query:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TREE ( p, ch ) AS
SELECT 1, 2 FROM DUAL
UNION ALL SELECT 1, 3 FROM DUAL
UNION ALL SELECT 1, 4 FROM DUAL
UNION ALL SELECT 2, 5 FROM DUAL
UNION ALL SELECT 2, 6 FROM DUAL
UNION ALL SELECT 7, 8 FROM DUAL
UNION ALL SELECT 9, 10 FROM DUAL
UNION ALL SELECT 11, 12 FROM DUAL
UNION ALL SELECT 6, 13 FROM DUAL
UNION ALL SELECT 13, 14 FROM DUAL
UNION ALL SELECT 14, 15 FROM DUAL
UNION ALL SELECT 14, 16 FROM DUAL
UNION ALL SELECT 2, 1 FROM DUAL;
Query 1:
SELECT p AS ParentChilds
FROM TREE
START WITH p = 1
CONNECT BY NOCYCLE PRIOR ch = p
UNION
SELECT ch
FROM TREE
START WITH p = 1
CONNECT BY NOCYCLE PRIOR ch = p
UNION
SELECT p
FROM TREE
START WITH p = 1
CONNECT BY NOCYCLE ch = PRIOR p
Results:
| PARENTCHILDS |
|--------------|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 13 |
| 14 |
| 15 |
| 16 |
Query 2:
SELECT p AS ParentChilds
FROM TREE
START WITH p = 6
CONNECT BY NOCYCLE PRIOR ch = p
UNION
SELECT ch
FROM TREE
START WITH p = 6
CONNECT BY NOCYCLE PRIOR ch = p
UNION
SELECT p
FROM TREE
START WITH p = 6
CONNECT BY NOCYCLE ch = PRIOR p
Results:
| PARENTCHILDS |
|--------------|
| 1 |
| 2 |
| 6 |
| 13 |
| 14 |
| 15 |
| 16 |
This approach may be slightly easier to read as it uses a more traditional recursive cte approach, by starting at the root and joining each parent to its children. Root nodes are identified as those nodes which themselves have no parents (where not exists).
with LinkedAccounts(topRoot, parent, child, lvl)
as
(
select r.p as topRoot, r.p as parent, r.ch as child, 1 as lvl
from tree r
where not exists
(
select 1 from tree t where r.p = t.ch
)
union all
select la.topRoot
, ch.p
, ch.ch
, la.lvl + 1
from LinkedAccounts la
inner join tree ch
on ch.p = la.child
),
ParentAndChild as
(
select topRoot, parent as node, lvl from LinkedAccounts
union all
select topRoot, child as node, lvl from LinkedAccounts
)
select distinct node
from ParentAndChild
where topRoot = 1
order by node ASC;
SqlFiddle here
As an aside, the naming convention in your hierarchy is unusual - ch would typically be nodeid (but parent would be parent / parentid) - this would then model a node allowing for additional columns on the node. At first glance it appears that you are modelling the connection relationship itself, not a node?
Here is my table:
parent_id | child_id
--------------
1 | 2
1 | 3
1 | 4
2 | 5
2 | 6
5 | 8
8 | 9
9 | 5
I need to get all of the items under parent 2. I've found a few things similar to this, but but couldn't figure out how to make it work for my case. I keep getting maximum recursion limit reached. Here's what I have:
WITH CTE AS
(
SELECT gt.[child_id]
FROM [CHSPortal].[dbo].[company_adgroupstoadgroups] gt
WHERE gt.parent_id='2'
UNION ALL
SELECT g.[child_id]
FROM [CHSPortal].[dbo].[company_adgroupstoadgroups] g
INNER JOIN CTE g2 on g.parent_id=g2.child_id
)
select distinct child_id from CTE
The desired result is going to be: 2,3,4,5,6,8,9.
What modification do I need to make to get a list of all the items under child 2. I would also prefer 2 (the parent node) to be in the list. Any help would be appreciated.
First of all, there is a loop in your example (5|8, 8|9, 9|5), that is why you reach the maximum recursion limit.
Regarding the filtering question,below you can find an example for filtering by root node:
;WITH MTree (parent_id, child_id, LEVEL) AS (
SELECT t.parent_id , t.child_id, 0 AS LEVEL
FROM table_1 t
WHERE child_id = 2 --here you can filter the root node
UNION ALL
SELECT m.parent_id , m.child_id, LEVEL + 1
FROM Table_1 m
INNER JOIN MTree t ON t.child_id = m.parent_id
)
SELECT * FROM Mtree;
Not sure what's wrong with your query, aside from not relating to the sample data you provided, but this works just fine:
;WITH src AS (SELECT 1 AS parent_id, 2 AS child_id
UNION SELECT 1, 3
UNION SELECT 1, 4
UNION SELECT 2, 5
UNION SELECT 2, 6
UNION SELECT 5, 8
UNION SELECT 8, 9
UNION SELECT 9, 5)
,cte AS (SELECT *
FROM src
WHERE child_id = 2
UNION ALL
SELECT a.*
FROM src a
JOIN cte b
ON a.parent_id = b.child_id
)
SELECT TOP 100 *
FROM cte
--Limited to top 100 because of infinite recursion problem with sample data.