Get all parents for a child - sql-server

I want to retrieve the parentid of an id, if that parentid has a parent again retrieve it, and so on.
Kind of hierarchy table.
id----parentid
1-----1
5-----1
47894--5
47897--47894
am new to sql server and tried, some queries like:
with name_tree as
(
select id, parentid
from Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select c.id, c.parentid
from users c
join name_tree p on p.id = c.parentid -- this is the recursion
)
select *
from name_tree;
It is giving me only one row.
and also I want to insert these records into a temporary table variable.
How can I do this. thanks in advance. sorry for asking the simple question(though not to me)

Try this to get all parents of a child
;with name_tree as
(
select id, parentid
from Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select C.id, C.parentid
from Users c
join name_tree p on C.id = P.parentid -- this is the recursion
-- Since your parent id is not NULL the recursion will happen continously.
-- For that we apply the condition C.id<>C.parentid
AND C.id<>C.parentid
)
-- Here you can insert directly to a temp table without CREATE TABLE synthax
select *
INTO #TEMP
from name_tree
OPTION (MAXRECURSION 0)
SELECT * FROM #TEMP
Click here to view result
EDIT :
If you want to insert into a table variable, you can do something like:
-- Declare table varialbe
Declare #TABLEVAR table (id int ,parentid int)
;with name_tree as
(
select id, parentid
from #Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select C.id, C.parentid
from #Users c
join name_tree p on C.id = P.parentid -- this is the recursion
-- Since your parent id is not NULL the recursion will happen continously.
-- For that we apply the condition C.id<>C.parentid
AND C.id<>C.parentid
)
-- Here you can insert directly to table variable
INSERT INTO #TABLEVAR
select *
from name_tree
OPTION (MAXRECURSION 0)
SELECT * FROM #TABLEVAR
Click here to view result

Your query is doing recursion but in opposite direction. So if you change starting point to:
where id = 1
then you will have user 1 and all his successors

you didn't mention the desired output and input.
However you can try like this,
Declare #t table (id int ,parentid int)
insert into #t
select 1,1 union all
select 5,1 union all
select 47894,5 union all
select 47897,47894
;With CTE as
(
select * from #t where id=1
union all
Select a.* from #t a inner join cte b
on b.id=a.parentid and
a.id<>b.id
)
select * from cte

Related

sql server using recrusive cte to get the level in the same group

I have a sql server table showing the IDs and their previous IDs,
create table test2 ( ID varchar(10) ,
Pre_ID varchar(10)
)
insert into test2 values ('e','d')
, ('d','c')
, ('c','b')
, ('b','a')
, ('a',null)
, ('r','q')
, ('q','p')
, ('p',null)
the table is like this:
The result should be like this:
I have successfully got the levels using a recursive cte, but could not get the correct group for them. can anyone help? Thanks.
This is my code:
with cte as (
select id, Pre_ID, level
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id
What you need to do is start with the first level and add a ROW_NUMBER to that, then join all further levels recursively:
with cte as (
select id, Pre_ID, level, row_number() over (order by ID) as grp
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level, cte.grp
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id;

Fill a table variable using CTE

I am creating a CTE as I need all InquiryId in a variable in order to proceed to a WHILE loop.
I am trying to fill a variable via a CTE like below
DECLARE #inqIdsToClose TABLE (InquiryId int)
With CTE (InquiryId,InquirySubject,CreateDt,SendTo,[From],SendBy) as
(
Select I.InquiryId,I.InquirySubject,I.CreateDt,I.SendTo,I.[From],U.Email as SendBy
From Inquiries I
Inner Join Users U
ON I.[From] = U.UserID
Where I.InquiryId Not In (Select InquiryId from InquiryReply)
And I.InquiryStatusId <> 5
And DATEDIFF(day, I.CreateDt, getdate()) >=7
)
Insert into #inqIdsToClose
Select InquiryId from CTE
Print #inqIdsToClose;
But SQL Server is not allowing me to fill #inqIdsToClose
The InquiryId which are returned by CTE are multiple which I will later use in a while loop.
You were missing the terminator after you declared your table variable
Example
DECLARE #inqIdsToClose TABLE (InquiryId int);
with cte as (
-- Dummy query
Select SomeVal=1
Union All
Select SomeVal=2
)
Insert Into #inqIdsToClose Select SomeVal from cte
Select * from #inqIdsToClose
Returns
InquiryId
1
2

How to update a hierarchyid column

I have a table called items which has a parent/child relation that I am converting to a hierarchyid.
I have followed this tutorial to do so.
All the steps of the tutorial works, except the final update statement.
I get the error message:
Implicit conversion from data type hierarchyid to nvarchar(max) is not allowed. Use the CONVERT function to run this query.
But this makes no sense. The field I am updating is a hierarchyid, not an nvarchar(nax). So I don't see where an nvarchar(max) field is involved.
drop table #children
CREATE TABLE #Children
(
ID int,
TenantId int,
ParentID int,
Num int
);
GO
CREATE CLUSTERED INDEX tmpind ON #Children(TenantId, ParentID, ID);
GO
INSERT #Children (ID, TenantId,ParentID, Num)
SELECT ID, TenantId, ParentID,
ROW_NUMBER() OVER (PARTITION BY TenantId, ParentID ORDER BY ParentId)
FROM Items
GO
SELECT * FROM #Children ORDER BY TenantId, ParentID, Num
GO
WITH paths(path, ID, ParentId, TenantId)
AS (
-- This section provides the value for the root of the hierarchy
SELECT hierarchyid::GetRoot() AS OrgNode, ID, ParentId, TenantId
FROM #Children AS C
WHERE ParentId IS NULL
UNION ALL
-- This section provides values for all nodes except the root
SELECT
CAST(p.path.ToString() + CAST(C.Num AS varchar(30)) + '/' AS hierarchyid),
C.ID , C.ParentId, C.TenantId
FROM #Children AS C
JOIN paths AS p
ON C.ParentID = P.ID
)
-- This select statement runs just fine and shows expected data.
--Select i.Id as ItemId, p.path, p.path.ToString() as LogicalNode, p.Id, p.ParentId, p.TenantId from Paths P
--join Items I on p.Id = i.Id
--order by P.TenantId, P.path
--Note that I have tried using the convert function, but it still fails with the same error message.
UPDATE I Set OrgNode = Convert(hierarchyid, P.path)
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
GO
EDIT
Strangely, this DBFiddle works.
It looks like column OrgNode is not of type hierachyid. You could use ToString()
UPDATE I Set OrgNode = P.path.ToString()
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
or alter table Items and change column type.
It looks like you solved your problem, but I would suggest saving the conversion to hierarchyid to the end. Like this:
WITH paths(path, ID, ParentId, TenantId)
AS (
-- This section provides the value for the root of the hierarchy
SELECT cast('/' as varchar(max)) AS OrgNode, ID, ParentId, TenantId
FROM #Children AS C
WHERE ParentId IS NULL
UNION ALL
-- This section provides values for all nodes except the root
SELECT
CAST(concat(p.path.ToString(), C.Num, '/') AS varchar(max)),
C.ID , C.ParentId, C.TenantId
FROM #Children AS C
JOIN paths AS p
ON C.ParentID = P.ID
)
-- This select statement runs just fine and shows expected data.
--Select i.Id as ItemId, p.path, p.path.ToString() as LogicalNode, p.Id, p.ParentId, p.TenantId from Paths P
--join Items I on p.Id = i.Id
--order by P.TenantId, P.path
--Note that I have tried using the convert function, but it still fails with the same error message.
UPDATE I Set OrgNode = Convert(hierarchyid, P.path)
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
GO
Note, I also changed the + style of concatenation for the concat() function so you don't have to mess around with converting C.Num to a varchar.

Select record from SQL Server like data below

I have record like below in SQL Server.
Id RefId FromRefId
1 RH01 RH00
2 RH02 RH01
3 RH03 RH01
4 RH04 RH03
5 RH05 RH02
6 RH06 RH03
7 RH07 RH04
8 RH08 RH02
9 RH09 RH05
And I want get result like below using Id in where condition
Where Id=1
RH02
RH03
RH04
RH05
RH06
RH07
RH08
RH09
Where Id=2
RH05
RH08
RH09
Where Id=3
RH04
RH06
RH07
Where Id=4
RH07
Where Id=5
RH09
Thanks, please guide me how can I achieve this?
Since you want to obtain all the references following the chain of FromRefId you need to use a recursive query, which can be achieved in SQL Server using a recursive common table expression:
with Recursive_IDs (Id, RefId, FromRefId) as (
-- anchor query
select Id, RefId, FromRefId
from IDs
union all
-- recursive query
select IDs.Id, IDs.RefID, Recursive_IDs.FromRefId
from IDs
inner join Recursive_IDs on Recursive_IDs.RefId=IDs.FromRefId
)
select Recursive_IDs.RefId
from Recursive_IDs
join IDs on Recursive_IDs.FromRefID=IDs.RefID
where IDs.id = [the id you want]
SQL fiddle
Note that if instead of searching by Id you search by RefId you can simplify the query a bit:
with Recursive_IDs (Id, RefId, FromRefId) as (
-- anchor query
select Id, RefId, FromRefId
from IDs
union all
-- recursive query
select IDs.Id, IDs.RefID, Recursive_IDs.FromRefId
from IDs
inner join Recursive_IDs on Recursive_IDs.RefId=IDs.FromRefId
)
select Recursive_IDs.RefId
from Recursive_IDs
where FromRefId = [the RefId you want]
You can use the below approach. I have written a Table-valued function, "GetChild". It iterates through the records recursively to get all dependencies, and finally get the RefId for all those dependencies.
Create table hierarchy (Id int, RefId varchar(10), FromRefId varchar(10))
GO
insert into hierarchy
select 1,'RH01','RH00' union all
select 2,'RH02','RH01' union all
select 3,'RH03','RH01' union all
select 4,'RH04','RH03' union all
select 5,'RH05','RH02' union all
select 6,'RH06','RH03' union all
select 7,'RH07','RH04' union all
select 8,'RH08','RH02' union all
select 9,'RH09','RH05'
GO
-- Table valued Function
GO
create function GetChild (#Id INT)
RETURNS #temp TABLE (RefId varchar(10))
AS
BEGIN
declare #tempDependencies table (Id int)
insert into #tempDependencies SELECT #Id
WHILE ((Select COUNT(Id) from hierarchy where FromRefId in (select RefId from hierarchy where id in (select Id from #tempDependencies) ) and id not in (select Id from #tempDependencies)) > 0)
BEGIN
insert into #tempDependencies
Select Id from hierarchy where FromRefId in (select RefId from hierarchy where id in (select Id from #tempDependencies) ) and id not in (select Id from #tempDependencies)
END
insert into #temp
Select RefId from hierarchy where FromRefId in (select RefId from hierarchy where id in (SELECT Id from #tempDependencies))
return
END
GO
-- You may call the functions like this:
select * from GetChild(1)
select * from GetChild(2)
select * from GetChild(3)
SQL Fiddle Code
Should be a simple query
SELECT * FROM your_table_name WHERE Id = your_desired_id
Why the downgrade? Isn’t that what you were looking for? I don’t think your question is clear. Which Id is being referred here?!

How to create an SQL Server 2005 CTE to return parent-child records, for children with multiple parents

I'm experimenting with CTE's in SQL Server but have reached a dead end with getting the following scenario to work. I have a hierarchy table similar to this:
Node(ID:439)
Node(ID:123)
Node(ID:900)
Node(ID:56)
Node(ID:900)
Expected results:
NodeID ParentNodeID
439 0
123 439
900 123
56 439
900 56
So basically we have a parent-child hierarchy table, with one subtle difference. Each child could potentially have more then one parent. I have researched many blog articles, and StackOverflow posts, about creating CTE's that return parent-child records, but they don't return all of the parents for the children, just the first one that it finds.
Here's an example CTE that I tried:
WITH Hierarchy(NodeID, ParentNodeID)
AS
(
SELECT
T1.NodeID,
T1.ParentNodeID
FROM
ParentChildTable T1
WHERE
T1.NodeID = 439
UNION ALL
SELECT
T1.NodeID,
T1.ParentNodeID
FROM
Heirarchy T1
INNER JOIN Heirarchy TH ON TH.NodeID = T1.ParentNodeID
)
(Note: The names of the tables and columns in the above CTE have been changed from the orginal for privacy purposes.)
The above CTE works fine, it finds all the parent-child records starting from ID:439, but it only finds one parent for item ID:900, even though it has two parents.
Could someone let me know if this is possible using CTE's, or is there another SQL way to do this?
Cheers.
Jas.
This appears to work OK for me, once I corrected the syntax error in your CTE:
create table #ParentChildTable
(nodeID int not null
,parentNodeID int not null
)
insert #ParentChildTable
select 900,56
union all select 900,123
union all select 123,439
union all select 56,439
union all select 439,0
;WITH Heirarchy
AS
(
SELECT
T1.NodeID,
T1.ParentNodeID
FROM
#ParentChildTable T1
WHERE
T1.NodeID = 439
UNION ALL
SELECT
T1.NodeID,
T1.ParentNodeID
FROM
#ParentChildTable T1
INNER JOIN Heirarchy TH ON TH.NodeID = T1.ParentNodeID
)
select *
from Heirarchy
Returns the result:
NodeID ParentNodeID
----------- ------------
439 0
123 439
56 439
900 56
900 123
This is the link I used to find a solution..
http://wiki.lessthandot.com/index.php/Using_Common_Table_Expressions_for_Parent-Child_Relationships
EDIT - #Pshimo - thanks. Heres the guide form the link.
With SQL 2005 though it is a dream. Say you have this sample data:
declare #test table (bunchof uniqueidentifier default newid(), columns uniqueidentifier default newid(), Id int, ParentID int)
insert #test (Id, ParentId)
select 1, null
union all select 5, 1
union all select 15, 2
union all select 16, 5
union all select 27, 16
And you want to get all child rows for 1 (so ItemId 5, 16, 27)
declare #parentId int
set #parentId = 1
;--last statement MUST be semicolon-terminated to use a CTE
with CTE (bunchof, columns, Id, ParentId) as
(
select bunchof, columns, Id, ParentId
from #test
where ParentId = #parentId
union all
select a.bunchof, a.columns, a.Id, a.ParentId
from #test as a
inner join CTE as b on a.ParentId = b.Id
)
select * from CTE
and if you want to include the parent:
declare #Id int
set #Id = 1
;--last statement MUST be semicolon-terminated to use a CTE
with CTE (bunchof, columns, Id, ParentId) as
(
select bunchof, columns, Id, ParentId
from #test
where Id = #Id
union all
select a.bunchof, a.columns, a.Id, a.ParentId
from #test as a
inner join CTE as b on a.ParentId = b.Id
)
select * from CTE
You can select depth in the hierarchy as well, if you're into that kind of thing:
declare #Id int
set #Id = 1
;--last statement MUST be semicolon-terminated to use a CTE
with CTE (bunchof, columns, Id, ParentId, Depth) as
(
select bunchof, columns, Id, ParentId, 0
from #test
where Id = #Id
union all
select a.bunchof, a.columns, a.Id, a.ParentId, b.Depth + 1
from #test as a
inner join CTE as b on a.ParentId = b.Id
)
select * from CTE
As you can see what you're doing here is first selecting your initial recordset, which contains all child rows for your parent ID parameter. You can then union to another query that joins to the CTE itself, to get the children's children (and their grandchildren, and so forth until you reach the last descendant row. Its' important to note that the default recursion limit is 100, so pay attention to the depth of your hierarchy when using these. You can change the recursion limit using the OPTION (MAXRECURSION)
WITH CTE AS (
...
)
SELECT * FROM CTE OPTION (MAXRECURSION 1000)

Resources