I have a data table like this
Entities:
ID | Parent_ID
1 | null
2 | 1
3 | 1
4 | 3
5 | 4
6 | 4
I'd like a sql expression that will return a row for every entity and a linear descendant, plus a row for null if the entity has no descendants. So given the above data my result set would be:
Entity | Descendant
1 | 2
1 | 3
1 | 4
1 | 5
1 | 6
2 | null
3 | 4
3 | 5
3 | 6
4 | 5
5 | null
6 | null
I tried using a common table expression to achieve this, and think it's the way to do it, given its ability to recurse, but I couldn't get my head wrapped around the spawning of many rows for a single parent.
with all_my_children (my_father,my_id,my_descendant,level)
as
(
select parent_id,id,null,0
from Entities
where id not in (select parent_id from entities)
union all
select e.parent_id,e.id,amc.my_id,amc.level+1
from Entities e
inner join all_my_children amc
on e.id = amc.my_father
WHERE ????? --How do I know when I'm done? and How do I keep repeating parents for each descendant?
)
select my_id, my_descendant from all_my_children
Thanks for your time.
Here's what you asked for
WITH TEMP AS
(
SELECT ID AS ENTITY, PID AS DESCENDANTS
FROM YPC_BI_TEMP.DBO.SV7104
WHERE PID IS NULL
UNION ALL
SELECT PID AS ENTITY, ID AS DESCENDANTS
FROM YPC_BI_TEMP.DBO.SV7104
WHERE PID IS NOT NULL
UNION ALL
SELECT PRNT.ENTITY, CHILD.ID AS DESCENDANTS
FROM YPC_BI_TEMP.DBO.SV7104 AS CHILD
INNER JOIN TEMP AS PRNT
on PRNT.DESCENDANTS = CHILD.PID
--AND PRNT.ENTITY IS NOT NULL
)
SELECT DISTINCT ENTITY, DESCENDANTS FROM TEMP
UNION
SELECT ID AS ENTITY, NULL AS DESCENDANTS FROM YPC_BI_TEMP.DBO.SV7104
WHERE ID NOT IN (SELECT ENTITY FROM TEMP)
Deleted my previous answer, but I think this might do the trick...
WITH all_my_children AS (my_father,my_id,my_descendant,level)
(
SELECT parent_id, id, null, 0
FROM Entities
WHERE parent_id IS NULL -- the roots of your tree
UNION ALL
SELECT COALESCE(e2.parent_id, e.parent_id), e.id, amc.my_id, amc.level+1
FROM Entities e
JOIN all_my_children amc
ON e.parent_id = amc.my_id
LEFT JOIN Entities e2
ON e.id = e2.parent_id
)
SELECT * FROM all_my_children
Related
I want to find out if users are directly in a parent child relation.
Given my user table schema
User_id | Parent_ID | Name
For example, I have a list of user_id's and I want to know if they are all in the same hierarchical tree.
I have tried using CTE recursive.
Sample data
User_id | Parent_ID | Name
1 | | A
2 | 1 | B
3 | 2 | C
4 | 3 | D
5 | 2 | E
6 | | F
7 | 6 | G
user_id varchar(100)
parent_id varchar(100)
Desired result: Input [2,3,4] => Same Team
Input [2,3,7] => Not same team
Use the top-level parents' parent_id as the hierarchy identifier:
with recursive hierarchies as (
select user_id, user_id as hierarchy_id
from ttable
where parent_id is null
union all
select c.user_id, p.hierarchy_id
from hierarchies p
join ttable c on c.parent_id = p.user_id
)
select * from hierarchies;
With that mapping of each user_id to a single hierarchy_id, you can join to your list of users.
EDIT BEGINS
Since you added sample data and example results that do not match your original question, here is an example of how any minimally competent programmer could slightly tweak the above to match the newly added contradictory examples:
with recursive subhierarchies as (
select user_id, array[user_id] as path
from ttable
where parent_id is null
union all
select c.user_id, p.path||c.user_id as path
from subhierarchies p
join ttable c on c.parent_id = p.user_id
)
select d.user_ids, count(s.path) > 0 as same_team
from (values (array[2, 3, 4]), (array[2, 3, 6])) as d(user_ids)
left join subhierarchies s
on s.path #> d.user_ids
group by d.user_ids
;
ENTRY TABLE
__________________
| ID | PARENT_ID |
| 1 | null |
| 2 | 1 |
| 3 | 2 |
| 4 | null |
| 5 | 4 |
| 6 | 5 |
...
I make copies of the entries in some cases and they are conneted by parent ID.
Each entry can have one copy:
THIS WONT HAPPEN
__________________
| ID | PARENT_ID |
| 1 | null |
| 2 | 1 |
| 3 | 1 |
...
Sometimes I need to take a copy and query for it's top level parent. I need to find the top parent entries for all the entries I search for.
For example, if I query for the parents of ID 6 and 3, I would get ID 4 and 1.
If I query for the parents of ID 5 and 2, I would get ID 4 and 1.
But also If I query for ID 5 and 1, it should return ID 4 and 1 because the entry ID 1 is already the top parent itself.
I don't know where to begin since I don't know how to recursively query in such case.
Can anyone point me in the right direction?
I know that the query below will just return the child elemements (ID 6 and 3), but I don't know where to go from here honestly.
I am using OracleSQL by the way.
SELECT * FROM entry WHERE id IN (6, 3);
You can use a hierarchical query and CONNECT_BY_ROOT.
Either starting at the root of the hierarchy and working down:
SELECT id,
CONNECT_BY_ROOT(id) AS root_id
FROM entry
WHERE id IN (6, 3)
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id;
Or, from the entry back up to the root:
SELECT CONNECT_BY_ROOT(id) AS id,
id AS root_id
FROM entry
WHERE parent_id IS NULL
START WITH id IN (6, 3)
CONNECT BY PRIOR parent_id = id;
Which, for the sample data:
CREATE TABLE entry( id, parent_id ) AS
SELECT 1, NULL FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL UNION ALL
SELECT 5, 4 FROM DUAL UNION ALL
SELECT 6, 5 FROM DUAL UNION ALL
SELECT 7, 6 FROM DUAL
Both output:
ID
ROOT_ID
3
1
6
4
db<>fiddle here
You can use recursive CTE to walk the graph and find the initial parent. For example:
with
n (starting_id, current_id, parent_id, v) as (
select id, id, parent_id, 0 from entry where id in (6, 3)
union all
select n.starting_id, e.id, e.parent_id, n.v - 1
from n
join entry e on e.id = n.parent_id
)
select starting_id, current_id as initial_id
from (
select n.*, row_number() over(partition by starting_id order by v) as rn
from n
) x
where rn = 1
Result:
STARTING_ID INITIAL_ID
------------ ----------
3 1
6 4
See running example at db<>fiddle.
I have a pretty complex table structure with parent-child relations.
The idea behind the structure is that some object in child_id can trigger a parent_id.
Assume this data;
Table 1 - map
map_id | parent_id | child_id
1 | 1 | 2
2 | 1 | 3
3 | 1 | 4
Table 2 - attributes
attribute_id | child_id | id_to_trigger
1 | 2 | 5
2 | 5 | 6
Example: A questionnaire system is a master. It can contain sub groups to be answered; in which case the sub groups become child of the master. Some answers in the sub groups can trigger an additional sub group within it.
I want to now be able to fetch all the sub group id's for a given master. A sub group can be triggered from multiple sub groups but that isn't a problem since I need just the sub group id's.
As you can tell, master with id 1 has 3 sub groups 2, 3, 4. In the attributes table we can see that sub group 2 can trigger sub group 5; similarly 5 can trigger 6 and so on.
I need 2, 3, 4, 5, 6 in my output. How do i achieve this?
Think about your design, i suggest that you dont need 2 tables if you add these 2 recs to your table 1
map_id | parent_id | child_id
1 | 1 | 2
2 | 1 | 3
3 | 1 | 4
4 | 2 | 5
5 | 5 | 6
you can now use a standard CTE to walk the tree
like this
with Tree as (select child_id from table_1 where parent_id = 1
union all
select table_1.child_id from table_1
inner join Tree on Tree.child_id = table_1.parent_id)
select * from Tree
if you cant change schema this will work
with
table_1 as ( select Parent_id , child_id from map
union all
select child_id as Parent_id, id_to_trigger as child_id from attributes)
,Tree as (select child_id from table_1 where parent_id = 1
union all
select table_1.child_id from table_1
inner join Tree on Tree.child_id = table_1.parent_id)
select * from Tree
Try this :
SELECT
map.parent_id,
map.child_id
FROM
map
UNION
SELECT
attributes.child_id,
attributes.id_to_trigger
FROM
map
Inner JOIN attributes ON map.child_id = attributes.child_id
UNION
SELECT
T1.child_id,
T1.id_to_trigger
FROM
attributes T1
Inner JOIN attributes T2 ON T1.child_id = T2.id_to_trigger
Result :
parent_id | child_id
1 | 2
1 | 3
1 | 4
2 | 5
5 | 6
I have a multilevel hierarchy data.
I want to combine everything into two level using sql query.
I want to return First parent and and Last Child of the Data
PID CId Name
Null 1 Electronics
1 2 Laptop
2 3 Toshiba
1 4 Mobile
4 5 Samsung
I need the result like this
PID CId Name
Null 1 Electronics
1 3 Toshiba
1 5 Samsung
Using this tutorial I create my first recursive CTE just for you.
The trick here is mark the row with null as the BigBoss and pass that value on the recursive secttion.
The last where have two filter
PID IS NULL to show the father item.
OR NOT EXISTS to show the items without childrens
SQL Fiddle Demo
Fiddle show your desire output, but if you unncoment the code you could see the full result to better understand.
WITH DirectReports (PID, CId, Name, Level, BigBoss)
AS
(
-- Anchor member definition
SELECT e.PID, e.CId, e.Name, 0 AS Level, e.CId BigBoss
FROM dbo.Table1 AS e
WHERE e.PID IS NULL
UNION ALL
-- Recursive member definition
SELECT e.PID, e.CId, e.Name, Level + 1, d.BigBoss
FROM dbo.Table1 AS e
INNER JOIN DirectReports AS d
ON e.PID = d.CId
)
-- Statement that executes the CTE
-- SELECT d.PID, d.CId, d.Name, d.Level, d.BigBoss
-- FROM DirectReports d
SELECT CASE
WHEN PID IS NULL THEN NULL
ELSE BigBoss
END PID,
Cid, Name
FROM DirectReports
WHERE
PID IS NULL
OR NOT EXISTS ( SELECT *
FROM Table1 t
WHERE d.CId = t.PID)
Result:
| PID | Cid | Name |
|--------|-----|-------------|
| (null) | 1 | Electronics |
| 1 | 5 | Samsung |
| 1 | 3 | Toshiba |
I am working on recursive query which take table with parent-child relation
ID | ParentID | description
1 | null | Company
2 | 1 | Department
3 | 2 | Unit1
4 | 2 | Unit2
5 | 4 | Unit3
6 | 4 | Unit4
and is suppose to display following result:
ID | ParentID | description
1 | null | Company
2 | 2 | Department
3 | 2 | Unit1
4 | 2 | Unit2
5 | 2 | Unit3
6 | 2 | Unit4
Of course the number of Deparments and units is larger. The basic quest is to display parentId for parent and its child level. Do you have any ideas how to achive this?
So far I only made this query
WITH cte (ID, ParentID, description)
AS
(
SELECT ID, ParentID, description
FROM T1
UNION ALL
SELECT e.ID, e.ParentID, e.description
FROM T2 AS e
JOIN cte ON e.ID = cte.ParentID
)
SELECT
cte.ID, cte.ParentID, cte.description
FROM cte
cte.ParentID is not null
Your syntax isn't quite right, but the idea is in the right direction. In the end, you want to fetch the rows where the parent's parent is NULL. This might work (it is untested):
WITH cte(ID, ParentID, description, lev) AS
(SELECT ID, ParentID, description, 1 as lev
FROM table T1
UNION ALL
SELECT cte.ID, e.ParentID, cte.description, cte.lev + 1
FROM table e JOIN
cte
ON e.ID = cte.ParentID
)
SELECT cte.ID, cte.ParentID, cte.description
FROM cte left outer join
table t
on cte.ParentId = t.ParentId
WHERE t.ParentID is null;