Delete all level child item using sql query - sql

I have a table where I have menus listed where I can insert and delete.
Structure goes like:-
ID Name ParentId
1 1. Home 0
2 2. Products 0
3 a. SubProduct1 2
4 b. SubProduct2 2
5 i. Subsub 4
6 ii. ...... 4
7 3. About 0
Top-level menu ParentId is always 0 as displayed in 1, 2 and 7.
Child level items would have ParentId of their parent for ex. Subproduct has 2 as its parentId.
When I delete menu item that time all level child item should be delete irrespective of there levels using SQL query.
There can be any number of levels
The levels can go upto subsubsubsub...... any number.

How about this query:
DECLARE #DelID INT
SET #DelID=1
;WITH T(xParent, xChild)AS
(
SELECT ParentID, ChildId FROM Table WHERE ParentID=#DelID
UNION ALL
SELECT ParentID, ChildId FROM TABLE INNER JOIN T ON ParentID=xChild
)
DELETE FROM TABLE WHERE ParentID IN (SELECT xParent FROM T)

You can use a common table expression to get all the heirarchy items from the item you want to delete to the end of the tree hten
;WITH ParentChildsTree
AS
(
SELECT ID, Name, ParentId
FROM MenuItems
WHERE Id = #itemToDelete
UNION ALL
SELECT ID, Name, ParentId
FROM ParentChildsTree c
INNER JOIN MenuItems t ON c.ParentId = t.Id
)
DELETE FROM MenuItems
WHERE ID IN (SELECT ID FROM ParentChildsTree);
Here is a Demo.
For example if you pass a parameter #itemToDelete = 4 to the query the the items with ids 2 and 4 will be deleted.

Related

Display Upper Most Hierarchy Parent for Child

I have a table with parent child relationships and I want to, for each child, display the upper most parent. The order of the real table won't be ordered so consecutively so any kind of sorting isn't desirable. I also want to avoid doing manual joins for the max number of hierarchy levels.
Table:
Parent Child
1 2
2 3
3 4
5 6
6 7
So for Child 4 has Parent 3, who has Parent 2, who has Parent 1.
4, 3, 2 should all have Parent 4 in the output.
Desired Output:
Parent Child
1 2
1 3
1 4
5 6
5 7
;with SelectedToTopCTE as
( select ParentID, ChildID as Child, 1 as level
from Table
union all
select d.ParentID, s.ChildID, d.level + 1
from SelectedToTopCTE as d
join Table s
on d.Child = s.ParentID
)
select * INTO #SelectedToTop
from SelectedToTopCTE;
SELECT Child, MAX(level) as MaxLevel INTO #UpperMostSPLT
FROM #SelectedToTop
group by Child;
SELECT A.*
FROM #SelectedToTop A
INNER JOIN
#UpperMostSPLT B
ON A.Child = B.Child AND A.level = B.MaxLevel
ORDER BY ParentID;
A recursive Common Table Expression (CTE) makes quick work of walking through parent/child hierarchies. This example starts with the top level parents, i.e. the rows that have no parents above them. It then adds children one level at a time while keeping track of the topmost parent.
-- Sample data.
declare #Samples as Table ( Parent Int, Child Int );
insert into #Samples ( Parent, Child ) values
( 1, 2 ), ( 2, 3 ), ( 3, 4 ), ( 5, 6 ), ( 6, 7 );
select * from #Samples;
-- Run the tree.
with Tree as (
-- Start with the top level parents.
select Parent as TopLevelParent, Child
from #Samples as S
-- Where the row has no parent above it.
where not exists ( select 42 from #Samples as SS where S.Parent = SS.Child )
union all
-- Add the children one level at a time.
select T.TopLevelParent, S.Child
from Tree as T inner join
#Samples as S on S.Parent = T.Child )
-- Display the sorted results.
select TopLevelParent, Child
from Tree
order by TopLevelParent, Child;

Using recursion with a self Foreign key

I'm using PostgreSQL 10, and I have the following structure:
A table Type with a foreign key to itself.
id name parent_id
1 namea Null
2 nameb Null
3 namea1 1
4 namea11 3
5 namea111 4
6 nameb1 2
7 nameb2 2
A table Item_Type for a Many to Many relation
id type_id item_id
1 1 1
2 3 2
3 5 3
4 7 4
Table Item which has M2M relation to Type.
id name
1 item1
2 item2
3 item3
4 item4
At this moment, I'm using an additional path field, which I calculate every time I make operations(crud) with Type.
I'm wondering if is not faster and easy to try to use the PostgreSQL recursion.
I checked the documentation but I didn't understand very well, because I get an error, and I don't understate why.
WITH RECURSIVE descendants AS (
SELECT id, name FROM Type WHERE id = 1
UNION
SELECT t.id, t.name, t.parent_id FROM Type AS t
INNER JOIN descendants AS d ON d.id = t.parent_id
) SELECT * FROM descendants;
ERROR: each UNION query must have the same number of columns
What I need - Giving a Type name:
1) Get all names/id for the requested Type and is descendants
2) Get all Item for the requested Type and is descendants, and the number of Item per Type and descendants
For example:
If the requested Type name is 'namea1', I should get for Type ids 1,3,4,5 and
for Item ids 1,2,3
The error says it all. Your union is divided between:
SELECT <2 fields> from Type ...
SELECT <3 fields> from Type JOIN Descendant ...
Simply select 3 fields on both halves:
WITH RECURSIVE descendants AS (
SELECT id, name, parent_id FROM Type WHERE id = 1
UNION
SELECT t.id, t.name, t.parent_id FROM Type AS t
INNER JOIN descendants AS d ON d.id = t.parent_id
) SELECT * FROM descendants;

What is the query to get parent records against specific child or get child records against parent?

Scenario:
I have data in the following hierarchy format in my table:
PERSON_ID Name PARENT_ID
1 Azeem 1
2 Farooq 2
3 Ahsan 3
4 Waqas 1
5 Adnan 1
6 Talha 2
7 Sami 2
8 Arshad 2
9 Hassan 8
E.g
Hassan is child of parent_id 8 which is (Arshad)
and Arshad is child of parent_id 2 which is (Farooq)
What I want:
First of all, I want to find all parent of parent of specific parent_id.
For Example: If I want to find the parent of Hassan then I also get the Parent of Hassan and also get its parent (Hassan -> Arshad -> Farooq)
Second, I want to find all child of Farooq like (Farooq -> Arshad -> Hassan)
Third, If Azeem is also have same parent like (Azeem -> Azeem) then show me this record.
What I've tried yet:
DECLARE #id INT
SET #id = 9
;WITH T AS (
SELECT p.PERSON_ID,p.Name, p.PARENT_ID
FROM hierarchy p
WHERE p.PERSON_ID = #id AND p.PERSON_ID != p.PARENT_ID
UNION ALL
SELECT c.PERSON_ID,c.Name, c.PARENT_ID
FROM hierarchy c
JOIN T h ON h.PARENT_ID = c.PERSON_ID)
SELECT h.PERSON_ID,h.Name FROM T h
and Its shows me below error:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
If I understand your question correctly that you don't want to insert null values in Parent_ID column then you should replace NULL with 0 and your updated code will be like:
;WITH DATA AS (
SELECT p.PERSON_ID,p.Name, p.PARENT_ID
FROM hierarchy p
WHERE p.PERSON_ID = 9
UNION ALL
SELECT c.PERSON_ID,c.Name, c.PARENT_ID
FROM hierarchy c
JOIN DATA h
ON c.PERSON_ID = h.PARENT_ID
)
select * from DATA;
You have an infinite loop in your data: Azeem is his own parent. You need to either make his value NULL or change your condition to WHERE p.parent_id = #id AND p.parent_id != p.child_id.
Also, I feel you have your columns named the wrong way around - the primary-key should be named person_id instead of parent_id and your column named child_id actually points to that person's parent, so it should be named parent_id instead.
Well I found a way for my above case which is:
If I have below table structure:
PERSON_ID Name PARENT_ID
1 Azeem NULL
2 Farooq NULL
3 Ahsan NULL
4 Waqas 1
5 Adnan 1
6 Talha 2
7 Sami 2
8 Arshad 2
9 Hassan 8
Then I tried below query which working fine in case when Parent_ID have NULL values means there is no more parent of that record.
DECLARE #id INT
SET #id = 2
Declare #Table table(
PERSON_ID bigint,
Name varchar(50),
PARENT_ID bigint
);
;WITH T AS (
SELECT p.PERSON_ID,p.Name, p.PARENT_ID
FROM hierarchy p
WHERE p.PERSON_ID = #id AND p.PERSON_ID != p.PARENT_ID
UNION ALL
SELECT c.PERSON_ID,c.Name, c.PARENT_ID
FROM hierarchy c
JOIN T h ON h.PARENT_ID = c.PERSON_ID)
insert into #table
select * from T;
IF exists(select * from #table)
BEGIN
select PERSON_ID,Name from #table
End
Else
Begin
select PERSON_ID,Name from Hierarchy
where PERSON_ID = #id
end
Above query show me the desire output when I set the parameter value #id = 1
Above query show me the desire output when I set the parameter value #id = 9
Issue:
I don't want to insert null values in Parent_ID like if there is no more Parent of that Person then I insert same Person_ID in Parent_ID column.
If I replace null values with there person_id then I got below error.
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

Algorithm behind Nested comments with SQL

I want to know the algorithm behind child nested records are displayed within parent nested records for example
Comment 1 (parent record)
reply 1 (child record)
reply 2 (child record)
reply 3 (child record)
view all
Comment 2 (parent record)
reply 1 (child record)
reply 2 (child record)
reply 3 (child record)
view all
how is a query written to get the above results?
You can use a Recursive Common Table Expression like the one below
;WITH comments AS
(
SELECT 1 as ID,'Comment 1' detail,NULL AS ParentID
UNION ALL SELECT 2 as ID,'Comment 2',NULL AS ParentID
UNION ALL SELECT 3 as ID,'Reply 1',1 AS ParentID
UNION ALL SELECT 4 as ID,'Reply 2',3 AS ParentID
UNION ALL SELECT 5 as ID,'Reply 3',4 AS ParentID
UNION ALL SELECT 6 as ID,'Reply 4',2 AS ParentID
UNION ALL SELECT 7 as ID,'Reply 5',6 AS ParentID
),comment_hierarchy AS
(
SELECT ID,detail,ID AS prid,0 AS orderid
FROM comments
WHERE ParentID IS NULL
UNION ALL
SELECT c.ID,c.detail ,ch.prid as prid,ch.orderid + 1
FROM comments c
INNER JOIN comment_hierarchy ch
ON c.ParentID = ch.ID
)
SELECT ID,Detail
FROM comment_hierarchy
ORDER BY prid,orderid asc
For more info refer
https://technet.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx
CTE to get all children (descendants) of a parent

recursive sql, sql server 2012

I'm trying to solve a recursive SQL problem essentially aggregating the status of a group of records.
For the purposes of the question - there are two tables. One that maintains the aggregation/hierarchy "GROUP_MEMBERS" and one that contains the individual items "ITEMS".
"GROUP_MEMBERS" looks similar (ID being GROUPID, CHILDTYPE being 0 for a group, 1 for individual item, and ID being the child items ID (so groupid for type 0, itemid for type 1)
ID | CHILDTYPE | CHILDID
1 0 2
1 1 1
2 1 2
2 1 3
2 1 4
In this example, my "ITEMS" table would have only two columns:
ID | STATUS
1 0
2 1
3 0
4 0
5 0
Effectively what I am trying to do is pull back all "ITEMS", ID and STATUS, under a group recursively (because groups can contain other groups). So for the example data I provided, if I passed it the GROUPID of 1, it would return ITEMS 1-4 with their statuses; GROUPID 2 would be ITEMS 2-4 with their statuses, etc.
I'm assuming I need to do this via a function and return a table, but I'm not even sure where to begin.
This was a nice puzzle, gotta tell ya :)
Hopefully it does you want it to do.
DECLARE #GroupRootID INT = 1
DECLARE #GROUP_MEMBERS TABLE (ID int, CHILDTYPE int, CHILDID int)
DECLARE #ITEMS TABLE (ID int, STATUS int)
INSERT INTO #GROUP_MEMBERS VALUES
(1,0,2), (1,1,1), (2,1,2), (2,0,3), (2,0,4), (2,1,3), (2,1,4), (3,1,5), (4,1,5)
INSERT INTO #ITEMS VALUES
(1,0), (2,1), (3,0), (4,0), (5,1)
-- 1
-- / \
-- 2 items items: 1 => 1,1,1
-- / | \
-- 3 4 items items: 2,3,4 => 2,1,2 - 2,1,3 - 2,1,4
-- | \
-- items items items (3): 5 => 3,1,5
-- items (4); 5 => 4,1,5
/* Recursivly build the GROUP tree (groups that have subgroups, CHILDTYPE=0), but NOT the lead nodes (CHILDTYPE = 1) */
;WITH GROUP_TREE
AS
(
/* SELECT all parents */
SELECT ParentGroups.*, 0 AS LEVEL
FROM #GROUP_MEMBERS AS ParentGroups
WHERE ParentGroups.CHILDTYPE = 0
AND ParentGroups.ID = #GroupRootID
UNION ALL
/* SELECT all childs groups for the parents */
SELECT ChildGroups.*, LEVEL + 1
FROM #GROUP_MEMBERS AS ChildGroups
INNER JOIN GROUP_TREE AS Parent ON Parent.CHILDID = ChildGroups.ID
WHERE ChildGroups.CHILDTYPE = 0
)
/* We now have all groups with their subgroups (not leaf nodes) */
/* Then join the leaf nodes (groups that are no subtree) */
/* Finally union the items from the root node and join the ITEMS to the leaf nodes to get the status */
/* Mind you though that ITEM 5 is linked double and will be returned NON-distinct */
SELECT ITEMS.*
FROM (
SELECT GROUPS.*
FROM #GROUP_MEMBERS AS GROUPS
INNER JOIN GROUP_TREE ON GROUP_TREE.CHILDID = GROUPS.ID
WHERE GROUPS.CHILDTYPE = 1
UNION ALL
SELECT GROUPS.*
FROM #GROUP_MEMBERS AS GROUPS
WHERE GROUPS.CHILDTYPE = 1
AND GROUPS.ID = #GroupRootID
) AS GROUP_ITEMS
INNER JOIN #ITEMS AS ITEMS ON GROUP_ITEMS.CHILDID = ITEMS.ID