SQL parent child recursive query - Includes Relatives relatives - sql

I have the following Table with two columns.
Parent Child
A C
A D
B A
B E
C H
J C
F G
G I
So I need to pass in a Parent of A and I am supposed to get the following back- All of a's Parent's and children but Also all of the Parents and Children to those associated to A (either parent or children) I need all their parents and children and so on.
SO in the example of A being passed to the proc I would get the following
A C
A D
B A
B E
C H
J C
If F was Passed I would just get
F G
G I

select * from test
where parent in (
select parent from test
where parent = 'F' or child = 'F'
union
select child from test
where child = 'F' or parent = 'F')
or child in (
select parent from test
where parent = 'F' or child = 'F'
union
select child from test
where child = 'F' or parent = 'F');

Try this, it is similar to the link given in the comments but accounts for both parent and child recursion. Hopefully it will work for you.
WITH relationships AS (
SELECT *
FROM Table
WHERE Child = 'A'
UNION ALL
SELECT p.*
FROM Table p
JOIN relationships pa on pa.Child = p.Parent
UNION ALL
SELECT c.*
FROM Table c
JOIN relationships ch on ch.Parent = c.Child
)
select *
from name_tree;

Related

Find all descendants of a parent in sql, including descendants of descendants

I want to find a table like this:
Parent
Child
A
B
A
C
A
D
A
E
B
C
B
D
So, the first level under A are nodes B and C. And under B is another level of nodes including C and D, however I also want these nodes included as rows under A.
The available dataset consists out of nodes with their level.
Node
Level
A
1
B
2
C
3
D
3
E
2
And all the children below B are the rows until E as B and E have the same level.
Is it possible to create a table as I wanted from the information I have?
I have already looked into stored procedures but I have not used these before so I am a bit lost.
You may try the following self-join query:
select D.node Parent, T.node Child
from table_name T join table_name D
on T.level > D.level
order by D.node, T.node
If you want to select only one node from the nodes with the same level you may use the ROW_NUMBER() function as the following:
select D.node Parent, T.node Child
from table_name T join
(
select *,
row_number() over (partition by level order by node) rn
from table_name
) D
on T.level > D.level
where D.rn = 1
order by D.node, T.node
See a demo.

Find all ancestors without direct id-parentid in same table

I have a parent-child structure across two tables. The first table has BOM_ID for bills and ITEM_ID for associated children items. The other has BOM_ID and ITEM_ID for the parent item.
I am able to find the first level of parents' ITEM_ID with the following query
SELECT item_id
FROM bomversion
WHERE bom_id IN (SELECT bom_id FROM bom WHERE item_id = 1)
So, in order to find all ancestors, I would have to repeat this step. I've tried to look at CTE and recursion techniques but all examples have parentid and childid in the same table. I cannot figure this one out.
If 1 is child of 2, 2 is child of 3, 3 is child of 4, and 2 is also child of 5, I am looking for the following result:
ChildID
ParentID
1
2
2
3
2
5
3
4
The starting point will be a specific ChildID.
S O L U T I O N
Based on Adnan Sharif's proposal, I found the solution for my problem:
WITH items_CTE AS (
-- create the mapping of items to consider
SELECT
B.ITEMID AS Child_id,
BV.ITEMID AS Parent_Id
FROM BOM AS B
INNER JOIN BOMVERSION AS BV
ON B.BOMID = BV.BOMID
), parent_child_cte AS (
-- select those items as initial query
SELECT
Child_id,
Parent_id
FROM items_CTE
WHERE Child_id = '111599' -- this is the starting point
UNION ALL
-- recursive approach to find all the ancestors
SELECT
c.Child_Id,
c.Parent_Id
FROM items_CTE c
JOIN parent_child_cte pc
ON pc.Parent_Id = c.Child_id
)
SELECT * FROM parent_child_cte
From the dbfiddle that you shared in the comment, if I understand you correctly, you want to have the rows showing all the parents of a child.
For example, lets consider the hierarchy, 1 is a child of 2, 2 is a child of 3, and 3 is a child of 4. You want the final result as,
child_id
parent_id
1
2
1
3
1
4
2
3
2
4
3
4
If that's the case, we can use recursive CTE to build that table. First of all, we need to be build a relation between those two tables based on bom_id and then we need to find out parent-child relation. After that we will add rows based on the initial query. Please see the below code.
WITH RECURSIVE items_CTE AS (
-- create the mapping of items to consider
SELECT
B.Item_id AS Child_id,
BV.Item_id AS Parent_Id
FROM BOM AS B
INNER JOIN BOMVERSION AS BV
ON B.bom_id = BV.bom_id
), parent_child_cte AS (
-- select those items as initial query
SELECT
Child_id,
Parent_id
FROM items_CTE
UNION
-- recursive approach to find all the ancestors
SELECT
parent_child_cte.Child_Id,
items_CTE.Parent_Id
FROM items_CTE
INNER JOIN parent_child_cte
ON parent_child_cte.Parent_Id = items_CTE.Child_id
)
SELECT * FROM parent_child_cte

How to get all children of parent item when all are in the same row and table

I have a table of parts and sub-parts where each record contains the primary part for that record along with its ChildPart.
Part - ChildPart
A - B
A - C
A - D
C - F
C - Z
F - R
Z - R
Q - B
Q - C
So for the example above, part A has 7 total descendants (B, C, D, F, Z, R, R). A parent part can have multiple children and a child part can belong to more than 1 parent; notice that part B is used for both A and Q.
How can I efficiently show all the child parts of a given parent part just using joins and not using SQL cursors or loops? The hierarchical tree could theoretically be infinitely deep.
You can use a Recursive CTE:
DECLARE #pID VARCHAR(20) = 'A'
;WITH CTE AS (
SELECT ChildPart
FROM mytable
WHERE Part = #pID
UNION ALL
SELECT t1.ChildPart
FROM mytable AS t1
INNER JOIN CTE AS t2 ON t1.Part = t2.ChildPart
)
SELECT ChildPart
FROM CTE

SQL to select child rows only if all parents meet criteria

I have two tables Data and Parents. Basically what I want to do is make a search query that only selects a row from the Data table if all its parents have been tested (Tested = True). The following are examples of my tables:
Data
-------------------
ID Version Tested
A 1.1 True
B 1.2 True
C 0.2 False
D 0.6 False
-
Parents
-------------------
ChildID ParentID
C A
C B
D A
D B
D C
In this case C would only be returned in the query since it is the only one with parents that have all been tested:
Query
-------------------
ChildID Version Tested
C 0.2 False
Try:
select *
from data d
where not exists
(select 1
from parents p
where p.childid = d.id
and parentid in (select id from data where tested <> 'True'))
http://sqlfiddle.com/#!2/1bf20f/1/0
The only reason C is not the only one returned (A and B are also) is because A and B do not have any parents, do you want them to not be returned on that basis? (it is unknown whether all their parents have been tested because they have no parents)
If you do want them removed on that basis, use the following, and C will be the only returned:
http://sqlfiddle.com/#!2/1bf20f/7/0
select *
from data d
where not exists
(select 1
from parents p
where p.childid = d.id
and parentid in (select id from data where tested <> 'True'))
and id in (select childid from parents)

Select parent if all children meet criteria

I have tables set up like so:
Parent
------
id, ...
Child
-----
id, parent_id, x, y
I want to find the Parents, or the distinct parent_id(s), if all of the rows in Child containing a given parent_id meet a criteria involving x and y(in my case x = y).
For example:
Parent
------
id
1
2
3
Child
id, parent_id, x, y
1, 1, 2, 3
2, 1, 3, 4
3, 2, 5, 5
4, 2, 6, 7
5, 3, 8, 8
6, 3, 9, 9
would result in 3. Currently, I have a query that finds parent_ids that any of the children meet the criteria. I then use that to retrieve those records and check them in code if all the children meet the criteria. With the example data, I get parent_id 2 and 3, get the two parent records with all children, and evaluate. I want to do this with a single query, if possible.
You can use NOT EXISTS
SELECT id
FROM Parent p
WHERE NOT EXISTS
(
SELECT 1 FROM Child c
WHERE c.parent_Id = p.id
AND c.x <> c.y
)
Edit: Here's the sql-fiddle: http://sqlfiddle.com/#!3/20128/1/0
Should join 2 tables first because the parents does not have children that will satisfy
And should add index for pa_id column
SELECT DISTINCT pa.id
FROM pa INNER JOIN c ON c.pa_id = pa.id
WHERE NOT EXISTS ( SELECT 1 FROM c WHERE c.parent_Id = p.id and c.x <> c.y )
This is what you need?
select id from parent where id not in(
select parent_id from child
where x<>y
group by parent_id)
Old question, but I think it worth to give my 5 cents on this topic. I believe more efficient way is to use HAVING clause:
SELECT
Parent.id
FROM
Parent
JOIN Child ON Child.parent_id = Parent.id
GROUP BY
Parent.id
HAVING
SUM( CASE WHEN Child.x = Child.y THEN 1 ELSE 0 END) = COUNT( * )