TSQL ParentID Recursive SP - sql

I was wondering if someone could help me.
I have a table that is like this ...
ID ParentID
1 0
2 1
3 2
4 3
5 4
What I want to do is write a recursive TSQL statement that will get all of my parents ID. So for example, if I pass 5 into the stored procedure it would return 4,3,2,1.
Does anyone know how to do this? It would be very much appreciated if someone could help!

This is an example how to achieve this using recursive CTE:
DECLARE #id INT
SET #id = 5
CREATE TABLE #tmp (id INT , ParentId INT)
INSERT INTO #tmp VALUES(1,0)
INSERT INTO #tmp VALUES(2,1)
INSERT INTO #tmp VALUES(3,2);
INSERT INTO #tmp VALUES(4, 3);
INSERT INTO #tmp VALUES(5,4);
WITH parent AS
(
SELECT id, parentId from #tmp WHERE id = #id
UNION ALL
SELECT t.id, t.parentId FROM parent
INNER JOIN #tmp t ON t.id = parent.parentid
)
SELECT id FROM parent
WHERE id <> #id;
--OR
WITH parent AS
(
SELECT tmp1.id, tmp1.parentId from #tmp AS tmp1
INNER JOIN #tmp AS tmp2 ON tmp1.id = tmp2.parentId
WHERE tmp2.id = #id
UNION ALL
SELECT t.id, t.parentId FROM parent
INNER JOIN #tmp t ON t.id = parent.parentid
)
SELECT id FROM parent

Use a recursive common table expression :
http://msdn.microsoft.com/en-us/library/ms186243.aspx

Related

SQL Server hierarchy referencing and cross data referencing

This might be a stupid question, but I am not a DBA and kind of stuck with this issue. I have an application that trickles down all effects (asdf) under an applied ID (IDParent).
The data tables are setup like this:
Data Tables
3rd Data Table
I want to write a query that when using IDChild it will reference that entry's IDParent to get the parent ID while referencing it as an IDChild. For example for the data entry starting at 116 I want to use the parent ID (124) and get 321 in T1. I want to use this to get the RandoName associated with RandoID for all of the entries that has a parent ID of 321.
Right now I am using a script something like:
Select t.[NAME]
From T2 tv
Inner join T3 t on t.RandoID = tv.RandoId
Where
tv.IDChild = T1.IDChild OR tv.IDChild = T1.IDParent
but I'm not sure how to get the whole applied hierarchy.
This would yield something like this:
Resulting Query
PS. I can not change the tables/db schema. But maybe I can add one to do all the referencing? Please tell me what you think.
EDIT I'm sorry I forgot about this other stupid table that RandoID uses which contains the name of the RandoID. I am trying to get the name of RandoID
I think a loop could help you.
Try this:
CREATE TABLE #t1 (IDChild Int, IDParent Int);
CREATE TABLE #t2 (RandoID NVARCHAR(10) , IDChild Int);
CREATE TABLE #RandoName (RandoID NVARCHAR(10), RandoName VARCHAR(50));
INSERT INTO #t1 VALUES (321, NULL), (123,321),(124,123),(116,124)
INSERT INTO #t2 VALUES ('asdf', 123)
INSERT INTO #RandoName VALUES ('asdf', 'something')
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [RowNum], a.IDChild a, a.IDParent b, b.IDChild c INTO #t3 FROM #t1 a
LEFT OUTER JOIN #t1 b ON b.IDParent = a.IDChild
DECLARE #rownum INT;
DECLARE cbcursor CURSOR for Select RowNum FROM #t3;
OPEN cbcursor;
Fetch Next from cbcursor into #rownum
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #t3
SET c = (SELECT b from #t3 where RowNum = #rownum-1)
WHERE RowNum = #rownum
Fetch Next from cbcursor into #rownum;
END;
Close cbcursor;
Deallocate cbcursor;
SELECT a,b,t2.RandoID, r.RandoName FROM #t3
LEFT OUTER JOIN #t2 t2 on t2.IDChild = #t3.c OR t2.IDChild = #t3.b OR t2.IDChild = #t3.a
LEFT OUTER JOIN #RandoName r on t2.RandoID = r.RandoID
This is what I get:
If you have any changes in your tables, like more records for T2, this script should be modified.
Using recursion:
declare #t table (IDc int , Idp int)
insert into #t
values
(321,null)
,(123,321)
,(124,123)
,(116,124)
declare #t2 table (RandoID varchar(10), IDChild int)
insert into #t2
values('asdf',123)
;with cte as
(
select anchor = IDChild
,ParentOrSelf = IDc
,RandoID
,RandomName
from #t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) crossed
where IDc=#anchor
union all
select t2.IDChild
,IDc
, t2.RandoID,RandomName
from #t t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) t2
join cte on cte.ParentOrSelf = t.Idp
)
select IDc
, cte.RandoID,cte.RandomName
from #t t
left join cte on t.IDc = cte.ParentOrSelf
Results:
IDc RandoID
321 NULL
123 asdf
124 asdf
116 asdf

SQL select -one to many joins want to have the manys

I have two tables, TBL_PARENT (parentID, ParentName) and TBL_CHILDREN (ParentID,Child_Name)
A Parent can have 0 to many children
What I want is a query to give me a list of parent and their children in single row per parent.
For example
Parent1 John,Mary
Parent2 jane,steve,jana
And the number of rows to be the total number of parents
try this query :
I have created 3 table 2 of them are already created on your database #parant, #ch
and the third one is a temp table to put the result in.
create table #parant (id int , name varchar(10))
create table #ch (id int , name varchar(10), pid int)
insert into #parant select 1,'PA'
insert into #parant select 2,'PB'
insert into #parant select 3,'PC'
insert into #ch select 1,'Ca',1
insert into #ch select 1,'Cb',1
insert into #ch select 1,'Cc',1
insert into #ch select 1,'Cd',3
insert into #ch select 1,'Cf',3
insert into #ch select 1,'Ch',1
create table #testTable (id int, name varchar(10),chid int, chname varchar(10), cpid int)
insert into #testTable
select x.id , x.name ,isnull( y.id ,0), isnull(y.name,'') ,isnull(y.pid ,0)
from #parant as x
left outer join #ch as y
on x .id = y .pid
SELECT t.ID, t.name , STUFF(
(SELECT ',' + s.chname
FROM #TestTable s
WHERE s.ID = t.ID
FOR XML PATH('')),1,1,'') AS CSV
FROM #TestTable AS t
GROUP BY t.ID, t.name
GO
drop table #testTable
drop table #ch
drop table #parant
for the above data i got the following result
1 PA Ca,Cb,Cc,Ch
2 PB
3 PC Cd,Cf
SELECT COUNT(P.parentID),
P.ParentName,
C.Child_Name
FROM TBL_PARENT as P
INNER JOIN TBL_CHILDREN as C
WHERE P.parentID == c.ParentID
GROUP BY P.ParentName;
The line P.parentID == c.ParentID is doing the Join, and the line count(P.parentID) is doing the count of all the parents and the line GROUP BY P.ParentName is grouping all the rows by the name of the parent so you can display all the children of every single parent.

get all child from an parent id

hi i need a query to do this
my table data
ID ParentID DATA
--------------------------------
1 -1 a
2 1 b
3 2 c
4 3 d
5 3 f
and what ineed a query that take a ID as parameter and return all recursively childs and Itself
parameter : (ID=2)
return must be :
ID ParentID DATA
--------------------------------
2 1 b
3 2 c
4 3 d
5 3 f
Try this:
;with temp as (
select id, parentId, data from t
where id = 2
union all
select t.id, t.parentId, t.data from t
join temp on temp.id = t.parentId
)
select * from temp
Fiddle here.
This should do it for you:
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
where topparent = 2
drop table #temp
EDIT or you can put the WHERE clause inside the first select
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
WHERE id = 2
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
drop table #temp
Results:
id parentid data
2 1 b
3 2 c
4 3 d
5 3 f
declare #ID int = 2;
with C as
(
select ID, ParentID, DATA
from YourTable
where ID = #ID
union all
select T.ID, T.ParentID, T.DATA
from YourTable as T
inner join C
on T.ParentID = C.ID
)
select ID, ParentID, DATA
from C
Try on SE-Data
try this.
select * from table where id= 2 or parentid = 2

Recursive select in SQL

I have an issue I just can't get my head around. I know what I want, just simply can't get it out on the screen.
What I have is a table looking like this:
Id, PK UniqueIdentifier, NotNull
Name, nvarchar(255), NotNull
ParentId, UniqueIdentifier, Null
ParentId have a FK to Id.
What I want to accomplish is to get a flat list of all the id's below the Id I pass in.
example:
1 TestName1 NULL
2 TestName2 1
3 TestName3 2
4 TestName4 NULL
5 TestName5 1
The tree would look like this:
-1
-> -2
-> -3
-> -5
-4
If I now ask for 4, I would only get 4 back, but if I ask for 1 I would get 1, 2, 3 and 5.
If I ask for 2, I would get 2 and 3 and so on.
Is there anyone who can point me in the right direction. My brain is fried so I appreciate all help I can get.
declare #T table(
Id int primary key,
Name nvarchar(255) not null,
ParentId int)
insert into #T values
(1, 'TestName1', NULL),
(2, 'TestName2', 1),
(3, 'TestName3', 2),
(4, 'TestName4', NULL),
(5, 'TestName5', 1)
declare #Id int = 1
;with cte as
(
select T.*
from #T as T
where T.Id = #Id
union all
select T.*
from #T as T
inner join cte as C
on T.ParentId = C.Id
)
select *
from cte
Result
Id Name ParentId
----------- -------------------- -----------
1 TestName1 NULL
2 TestName2 1
5 TestName5 1
3 TestName3 2
Here's a working example:
declare #t table (id int, name nvarchar(255), ParentID int)
insert #t values
(1, 'TestName1', NULL),
(2, 'TestName2', 1 ),
(3, 'TestName3', 2 ),
(4, 'TestName4', NULL),
(5, 'TestName5', 1 );
; with rec as
(
select t.name
, t.id as baseid
, t.id
, t.parentid
from #t t
union all
select t.name
, r.baseid
, t.id
, t.parentid
from rec r
join #t t
on t.ParentID = r.id
)
select *
from rec
where baseid = 1
You can filter on baseid, which contains the start of the tree you're querying for.
Try this:
WITH RecQry AS
(
SELECT *
FROM MyTable
UNION ALL
SELECT a.*
FROM MyTable a INNER JOIN RecQry b
ON a.ParentID = b.Id
)
SELECT *
FROM RecQry
Here is a good article about Hierarchy ID models. It goes right from the start of the data right through to the query designs.
Also, you could use a Recursive Query using a Common Table Expression.
I'm guessing that the easiest way to accomplish what you're looking for would be to write a recursive query using a Common Table Expression:
MSDN - Recursive Queries Using Common Table Expressions

SQL Server: How to get all child records given a parent id in a self referencing table

Hi I have a table which references itself and I need to be able to select the parent and all it's child records from a given parent Id.
My table is as follows:
ID | ParentID | Name
-----------------------
1 NULL A
2 1 B-1
3 1 B-2
4 2 C-1
5 2 C-2
So for the above example I'd like to be able to pass in a value of 1 and get all the records above.
So far, I've come up with the following recursive table-valued-function but it's not behaving as expected (only returning the first record).
CREATE FUNCTION [dbo].[SelectBranches]
(
#id INT
,#parentId INT
)
RETURNS #branchTable TABLE
(
ID INT
,ParentID INT
,Name INT
)
AS
BEGIN
IF #branchId IS NOT NULL BEGIN
INSERT INTO #branchTable
SELECT
ID
,ParentID
,Name
FROM
tblLinkAdvertiserCity
WHERE
ID = #id
END
INSERT INTO #branchTable
SELECT
br.ID
,br.ParentID
,br.Name
FROM
#branchTable b
CROSS APPLY
dbo.SelectBranches(NULL, b.ParentID) br
RETURN
END
GO
You can try this
DECLARE #Table TABLE(
ID INT,
ParentID INT,
NAME VARCHAR(20)
)
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 1, NULL, 'A'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 2, 1, 'B-1'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 3, 1, 'B-2'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 4, 2, 'C-1'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 5, 2, 'C-2'
DECLARE #ID INT
SELECT #ID = 2
;WITH ret AS(
SELECT *
FROM #Table
WHERE ID = #ID
UNION ALL
SELECT t.*
FROM #Table t INNER JOIN
ret r ON t.ParentID = r.ID
)
SELECT *
FROM ret
Recursion in CTE looks bit expensive, so I have wrote this function which make use of recursive function call but much faster that CTE recursion.
CREATE FUNCTION [dbo].[Fn_GetSubCategories]
(
#p_ParentCategoryId INT
) RETURNS #ResultTable TABLE
(
Id INT
)
AS
BEGIN
--Insert first level subcategories.
INSERT INTO #ResultTable
SELECT Id FROM Category WHERE ParentCategoryId = #p_ParentCategoryId OR Id = #p_ParentCategoryId
DECLARE #Id INT
DECLARE #ParentCategory TABLE(Id INT)
DECLARE cur_categories CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY FOR
SELECT Id FROM Category WHERE ParentCategoryId = #p_ParentCategoryId and Id != #p_ParentCategoryId
OPEN cur_categories
IF ##CURSOR_ROWS > 0
BEGIN
FETCH NEXT FROM cur_categories INTO #Id
WHILE ##FETCH_STATUS = 0
BEGIN
--Insert remaining level sub categories.
IF EXISTS(SELECT 1 FROM Category WHERE ParentCategoryId = #Id AND Id != #Id)
BEGIN
INSERT INTO #ResultTable
SELECT DISTINCT C.Id from Fn_GetSubCategories(#Id) C INNER JOIN #ResultTable R ON C.Id != R.Id
END
FETCH NEXT FROM cur_categories INTO #Id
END
--Delete duplicate records
;WITH CTE AS
(SELECT *,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) AS RN FROM #ResultTable)
DELETE FROM CTE WHERE RN<>1
END
CLOSE cur_categories
DEALLOCATE cur_categories
RETURN
END
Unless you are using Oracle, your table structure is not suitable for the problem described. What you are attempting to do is grab a hierarchy (traversing a tree structure).
There is an article, More Trees & Hierarchies in SQL, that describes one method of solving the hierarchy problem. He basically adds a "lineage" column describing the hierarchy to every row.