How to select highest level from 2 different trees in same command - sql

I have a dataset that has the following appearence.
A 1
A 2
A 3
B 1
B 2
B 3
B 4
Which is a result from using the following command
select
connect_by_root id as root,
level lvl
from
dbset
start with id in ('A','B')
connect by nocycle child = prior parent)
I want the result
A 3
B 4
That is, I want to extract the topmost root in each of the trees. Ideally I would like this to be done within the same command I have, but I am too much of a novice within the area to know how to.

Use ROW_NUMBER:
SELECT root, lvl
FROM
(
SELECT
connect_by_root id AS root,
level lvl,
row_number() over (PARTITION BY connect_by_root id ORDER BY level DESC) rn
FROM dbset
START WITH id IN ('A','B')
CONNECT BY NOCYCLE child = prior parent
) t
WHERE rn = 1

Just use group by :
create table t23 ( dt varchar(10) ,id1 int );
insert all
into t23 values('A',1)
into t23 values('A',2)
into t23 values('A',3)
into t23 values('B',1)
into t23 values('B',2)
into t23 values('B',3)
into t23 values('B',4)
select * from dual;
select * from t23;
select dt , max(id1)id1 from t23 group by dt;

The connect_by_isleaf function might be what you're after, e.g.:
WITH dbset AS (SELECT 'A' ID, 1 CHILD, NULL PARENT FROM dual UNION ALL
SELECT 'A' ID, 2 CHILD, 1 PARENT FROM dual UNION ALL
SELECT 'A' ID, 3 CHILD, 2 PARENT FROM dual UNION ALL
SELECT 'B' ID, 1 CHILD, NULL PARENT FROM dual UNION ALL
SELECT 'B' ID, 2 CHILD, 1 PARENT FROM dual UNION ALL
SELECT 'B' ID, 3 CHILD, 2 PARENT FROM dual UNION ALL
SELECT 'B' ID, 4 CHILD, 3 PARENT FROM dual)
SELECT connect_by_root ID AS root,
LEVEL lvl,
CONNECT_by_isleaf,
CHILD, PARENT
FROM dbset
WHERE CONNECT_by_isleaf = 1
START WITH ID IN ('A', 'B') AND PARENT IS NULL
CONNECT BY NOCYCLE PRIOR CHILD = PARENT
AND ID = PRIOR ID
ORDER BY root;
ROOT LVL CONNECT_BY_ISLEAF CHILD PARENT
---- ---------- ----------------- ---------- ----------
A 3 1 3 2
B 4 1 4 3

This is basically the same as Tim Biegeleisen's answer, just completed with test data, result, sql fiddle and a correction about the partitioning condition.
Note: I am happily delete this answer as soon as Tim's answer is corrected and completed.
Test data:
create table dbset (id varchar(1), child varchar(1), parent varchar(1) );
insert into dbset values('A','A','A');
insert into dbset values('X','A','X');
insert into dbset values('Z','X','Z');
insert into dbset values('B','6','6');
insert into dbset values('G','6','7');
insert into dbset values('H','7','8');
insert into dbset values('I','8','9');
Query:
SELECT root, lvl
FROM
(
SELECT
connect_by_root id AS root,
level lvl,
row_number() over (PARTITION BY connect_by_root id ORDER BY level DESC) rn
FROM dbset
START WITH id IN ('A','B')
CONNECT BY NOCYCLE child = prior parent
) t
WHERE rn = 1;
Result:
| ROOT | LVL |
|------|-----|
| A | 3 |
| B | 4 |
Sql fiddle: http://sqlfiddle.com/#!4/37c70/8

Use ranking function row_number() to get the top most record for each root.
select * from
(
select *,
row_number() over (partition by id order by level desc) Seq
from table
) t
where Seq = 1

Related

Query to get all levels of children for a parent in a hierarchial table

My table structure is as below
ChildId
ParentId
1
3
2
3
3
4
4
null
Output must show all levels of children for a parent
ParentId
ChildId
4
4
4
1
4
2
4
3
3
3
3
1
3
2
2
2
1
1
So for every parent query has to show all the children so that if we specify any single parent in where clause, we can get all its levels of children. Would be still better if we could display level as well.
I tried using recursive CTEs but not able to achieve the required output
You have a strange requirement. In addition to traversing the hierarchy, you also want all nodes to be paired with themselves. This is not part of the original data, but can be added separately:
with cte as (
select parentId, childid
from t
where parentid is not null
union all
select cte.parentId, t.childid
from cte join
t
on t.parentId = cte.childId
)
select *
from cte
union all
select childid, childid
from t;
Here is a db<>fiddle.
Not sure why you want to add extra entries beyond the hierarchy, but you can do the levels bit like this..
declare #T table (
id integer identity(1,1) primary key,
ChildId Integer,
ParentId Integer
)
insert #T (ChildId, Parentid)
select 4,null
union select 3,4
union select 2,3
union select 1,3
;WITH CTE (ParentId, ChildId, Lvl) AS (
SELECT ParentId, ChildId, 1
FROM #T
WHERE ParentId IS NULL
UNION ALL
SELECT T.ParentId, T.ChildId, Lvl + 1
FROM #T T JOIN CTE E ON E.ChildId = T.ParentId
)
SELECT ChildId,ParentId,Lvl
FROM CTE
ORDER BY Lvl

Oracle table hierarchy level and grouping

I have a oracle database where foreign key is enabled and I have tables A,B,C,D,E,F,AB,ABC,EF
Attaching the picture for hierarchy
'B' has parent 'D',
'AB' has parent 'A' as well as 'B',
'ABC' has parent 'AB' and 'C',
'EF' has parent 'E' & 'F',
Now {A,B,C,D,AB, ABC} has no link with {E,F,EF}. I have 2 requirements
I need to group them as group 1 & 2
I need to assign a hierarchy level for each group maintaining referential integrity as follows
How can I do it using sql/pl/sql in oracle database?
Assuming that you always end up at a single leaf node for each group then you can use a hierarchical query twice: from leaf-to-root to determine the groups; and then from root-to-leaf to determine the depths.
WITH groups ( child, parent, grp ) AS (
SELECT child,
parent,
DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( child ) )
FROM table_name t
START WITH child NOT IN ( SELECT parent FROM table_name )
CONNECT BY PRIOR parent = child
),
depths ( child, parent, grp, depth ) AS (
SELECT child, parent, grp, LEVEL
FROM groups
START WITH parent NOT IN ( select child FROM table_name )
CONNECT BY PRIOR child = parent
)
SELECT table_name,
grp,
MAX( depth + depth_modifier ) AS depth
FROM depths
UNPIVOT ( table_name FOR depth_modifier IN ( child AS 1, parent AS 0 ) )
GROUP BY grp, table_name
ORDER BY grp, depth, table_name
Which, for your sample data:
CREATE TABLE table_name ( child, parent ) AS
SELECT 'B', 'D' FROM DUAL UNION ALL
SELECT 'AB', 'A' FROM DUAL UNION ALL
SELECT 'AB', 'B' FROM DUAL UNION ALL
SELECT 'ABC', 'AB' FROM DUAL UNION ALL
SELECT 'ABC', 'C' FROM DUAL UNION ALL
SELECT 'EF', 'E' FROM DUAL UNION ALL
SELECT 'EF', 'F' FROM DUAL;
Outputs:
TABLE_NAME | GRP | DEPTH
:--------- | --: | ----:
A | 1 | 1
C | 1 | 1
D | 1 | 1
B | 1 | 2
AB | 1 | 3
ABC | 1 | 4
E | 2 | 1
F | 2 | 1
EF | 2 | 2
If you don't always have a single leaf node for each group then you'll need a MUCH more complicated solution (probably written in PL/SQL).
db<>fiddle here

determine no of layers of parent child relationship

Below is how data is structured in my table:
Notes:
P - indicates the parent
c - indicates the parent
A parent can have a parent ( having a child)
but a child can NOT have a child or either parent. Determine the layer/generations.
Column1,Column2
1-p,2-p:
2-p,3-p:
2-p,4-c:
3-p,5-c
how to read the above data:
1 has a child named 2;
2 has child 3 & 4;
3 has child 5.
So based on the above logic '1' has 3 layers.
How to write a query to determine no of layers in Oracle?
You can use a hierarchical query:
SELECT column1,
MAX( depth ) AS max_depth
FROM (
SELECT CONNECT_BY_ROOT( column1 ) AS column1,
LEVEL AS depth
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY
PRIOR Column2 = Column1
)
GROUP BY
column1;
Which, for the sample data:
CREATE TABLE table_name ( Column1, Column2 ) AS
SELECT '1-p','2-p' FROM DUAL UNION ALL
SELECT '2-p','3-p' FROM DUAL UNION ALL
SELECT '2-p','4-c' FROM DUAL UNION ALL
SELECT '3-p','5-c' FROM DUAL;
Outputs:
COLUMN1 | MAX_DEPTH
:------ | --------:
1-p | 3
3-p | 1
2-p | 2
db<>fiddle here

Connect by prior get parents and children

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).

oracle 9i get highest member of tree with given child

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