Get the hierarchical data but data has not straight forward - sql-server

We have requirement like to get the hierarchical data but data has not straight forward:
Eg:
Level
1
2
3
4
4
5
6
7
7
7
5
5
8
8
9
Here 1 is parent for child 2
2 is parent for child 3
3 is parent for children 4,4,4
last 4 is parent for child 5
5 is parent for child 6
6 is parent for children 7, 7,7
again 5 is child for last parent 4
last 7 is parent for child 8
Please help me how to write a query to get the above result for the above one

How to recognize last 5 or last 7 ?
If you some other info in table which tell this then it is more nice.
Else we can find of our own also but that is little expensive.
Try this,
create table #temp( Levels int)
insert into #temp values
(1),(2),(3),(4)
,(4),(5),(6),(7)
,(7),(7),(5),(5)
,(8),(8),(9)
;With CTE as
(
select *
,ROW_NUMBER()over(order by (select null))rn
from #temp
)
select * into #temp1
from CTE
;With CTE1 as
(
select *
,ROW_NUMBER()over(partition by levels
order by rn desc)rn1
from #temp1
)
select c.*
,c1.Levels as Parentid
from CTE1 c
outer apply(select top 1 c1.Levels from CTE1 c1
where c1.Levels<c.Levels and c1.rn1=1-- last parent
order by c1.Levels desc)c1
order by c.rn
drop table #temp,#temp1
Let me know if it do not work for specific sample data.

Related

Classifying rows into a grouping column that shows the data is related to prior rows

I have a set of data that I want to classify into groups based on a prior record id existing on the newer rows. The initial record of the group has a prior sequence id = 0.
The data is as follows:
customer id
sequence id
prior_sequence id
1
1
0
1
2
1
1
3
2
2
4
0
2
5
4
2
6
0
2
7
6
Ideally, I would like to create the following grouping column and yield the following results:
customer id
sequence id
prior sequence id
grouping
1
1
0
1
1
2
1
1
1
3
2
1
2
4
0
2
2
5
4
2
2
6
0
3
2
7
6
3
I've attempted to utilize island gap logic utilizing the ROW_NUMBER() function. However, I have been unsuccessful in doing so. I suspect the need here is more along the lines of a recursive CTE, which I am attempting at the moment.
I agree that a recursive CTE will do the job. Something like:
WITH reccte AS
(
/*query that determines starting point for recursion
*
* In this case we want all records with no prior_sequence_id
*/
SELECT
customer_id,
sequence_id,
prior_sequence_id,
/*establish grouping*/
ROW_NUMBER() OVER (ORDER BY sequence_id) as grouping
FROM yourtable
WHERE prior_sequence_id = 0
UNION
/*join the recursive CTe back to the table and iterate*/
SELECT
yourtable.customer_id,
yourtable.sequence_id,
yourtable.prior_sequence_id,
reccte.grouping
FROM reccte
INNER JOIN yourtable ON reccte.sequence_id = yourtable.prior_sequence_id
)
SELECT * FROM reccte;
It looks like you could use a simple correlated query, at least given your sample data:
select *, (
select Sum(Iif(prior_sequence_id = 0, 1, 0))
from t t2
where t2.sequence_id <= t.sequence_id
) Grouping
from t;
See Example Fiddle

Find all parents of given ids in tree in SQL Server

I have a tree structure in single table and item relationships in another table. I need to find out all the parents from the given items.
SELECT Id FROM dbo.Items WHERE Selected = 1
Since I get 6,9, 11 from that query, I would need to return parent item ids of those which are 7, 5, 2.
I think I should use some kind of recursive CTE for this, but I'm not sure where to start.
Could you help me out? Thanks!
dbo.Relationship
Id ParentId
3 6
6 7
8 7
7 2
4 9
9 5
5 2
dbo.Items
Id Selected
2 0
3 0
4 0
5 0
6 1
7 0
8 0
9 1
11 1
Do you want to get all the parent of selected items?
I changed to use temporary table.
if object_id('tempdb..#Relationship') is not null drop table #Relationship
create table #Relationship(Id int,ParentId int)
insert into #Relationship(Id,ParentId)
SELECT 3,6 UNION
SELECT 6,7 UNION
SELECT 8,7 UNION
SELECT 7,2 UNION
SELECT 4,9 UNION
SELECT 9,5 UNION
SELECT 5,2
if object_id('tempdb..#items') is not null drop table #items
create table #items(Id int, Selected bit)
insert into #items(Id,Selected)
SELECT 2,0 UNION
SELECT 3,0 UNION
SELECT 4,0 UNION
SELECT 5,0 UNION
SELECT 6,1 UNION
SELECT 7,0 UNION
SELECT 8,0 UNION
SELECT 9,1 UNION
SELECT 11,1
;with cte AS (
SELECT i.ID AS SelectedID,r.ParentId FROM #Items AS i INNER JOIN #Relationship AS r ON i.id=r.id WHERE i.Selected=1
UNION ALL
SELECT cte.SelectedID, r.ParentId FROM #Relationship AS r INNER JOIN CTE ON CTE.ParentId=r.id
)
SELECT * FROM cte ORDER BY cte.SelectedID
Can it give you some help?
SelectedID ParentId
----------- -----------
6 7
6 2
9 5
9 2
Left Join on the related Id between Items and Relations.
SELECT
Items.Id,
Relationship.ParentId
FROM Items
LEFT JOIN Relationship ON Relationship.Id = Items.Id
I have used this in the past to get all parent Ids:
with compParent as
(
select * from Component Where ComponentId = #ComponentId
union all
select Component.* from Component join compParent on Component.ComponentId =
compParent.ContainerParentId
)
select * from compParent;
I have used this to get all children:
with compChild as
(
select * from Component where ComponentId = #ParentId
union all
select Component.* from Component join compChild on Component.ContainerParentId = compChild.ComponentId
)
select * from compChild;
You can also look at the many stackOverFlow posts that are already out there to get parent and/or children. Or a simple google search for "SQL Server Get Parents"

Roll up count in hierarchical table(self referential table)

I Have a table in the below format.This is a self referential table where each record points to its parent record.
NODE_ID PARENT_ID COUNT
1 0 NULL
2 1 NULL
3 2 10
4 2 12
5 0 NULL
6 5 NULL
7 6 NULL
8 7 12
I want the output to be in below format.The count of parent should be the sum of count of leaf childs.
Note: Only the leaf childs will contain the count. I want to roll it up till parents.
NODE_ID PARENT_ID COUNT
1 0 22
2 1 22
3 2 10
4 2 12
5 0 12
6 5 12
7 6 12
8 7 12
Please help.
Well, I couldn't think of anything simpler:
;WITH GetLevelsCTE AS (
SELECT NODE_ID, PARENT_ID, COUNT, level = 1, ROOT = NODE_ID
FROM mytable
WHERE PARENT_ID = 0
UNION ALL
SELECT t1.NODE_ID, t1.PARENT_ID, t1.COUNT, level = t2.level + 1, t2.ROOT
FROM mytable AS t1
JOIN GetLevelsCTE AS t2 ON t2.NODE_ID = t1.PARENT_ID
), MaxLevelCTE AS (
-- Get MAX level per root NODE_ID
SELECT MAX(level) AS max_level, ROOT
FROM GetLevelsCTE
GROUP BY ROOT
), GetCountCTE AS (
-- Anchor query: start from the bottom
SELECT t1.NODE_ID, t1.PARENT_ID, t1.COUNT, t1.level
FROM GetLevelsCTE AS t1
JOIN MaxLevelCTE AS t2 ON t1.ROOT = t2.ROOT
WHERE t1.level = t2.max_level
UNION ALL
-- Recursive query: get counts of next level
SELECT t1.NODE_ID, t1.PARENT_ID, t2.COUNT, t1.level
FROM GetLevelsCTE AS t1
JOIN GetCountCTE AS t2 ON t1.level = t2.level - 1 AND t1.NODE_ID = t2.PARENT_ID
)
SELECT NODE_ID, PARENT_ID, SUM(COUNT) AS COUNT
FROM GetCountCTE
GROUP BY NODE_ID, PARENT_ID
ORDER BY NODE_ID
Short explanation:
GetLevelsCTE is used to assign a level number to every node of the tree.
MaxLevelCTE uses the previous CTE in order the obtain the maximum level of the tree.
GetCountCTE uses both previous CTEs in order to traverse the tree from the bottom to the parent node. This way, the COUNT is propagated to the parent node.

Using Recursive CTE with GroupBy

I am new to the recursive CTE concept and a problem at hand, I got a tiny feeling that the problem can be solved by using recursive CTE. Let me know what you guys think.
Two tables:
Table one is a self referencing Location table with ID, ParentID, Level and Description.
Table two is an asset table which records individual assets and has a foreign key to Location table ID field.
Table1:
ID Description ParentID Level
1 Site1 NULL 1
2 Site2 NULL 1
3 Building1 1 2
4 Building2 1 2
5 Floor1 3 3
6 Floor2 3 3
7 Floor3 4 3
8 Place1 5 4
9 Place2 7 4
Table2:
ID Description Quantity LocationID
1 Desk 3 8
2 Lamp 1 8
3 PC 10 9
I would like to create a stored procedure with a input parameter of #Level and returns all the Location records at that level and the number of assets within the location (including sub levels).
For example, if #Level = 3, the stored procedure should return:
ID Description AssetCount
5 Floor1 4
6 Floor2 0
7 Floor3 10
If #Level = 2, the stored procedure should return:
ID Description AssetCount
3 Building1 4
4 Building2 10
If the problem is not clear, please let me know.
Well, nothing special here, just a recursive CTE joined with the other table, and the results are what you expected:
declare #level int = 3
;with CTE as (
select id as origid, id, Description, parentid
from table1 where level = #level
union all
select CTE.origid, t1.id, CTE.Description, t1.parentid
from CTE join table1 t1 on
CTE.id = t1.parentid
)
select origid, CTE.description, isnull(sum(t2.Quantity),0) as Quantity
from CTE left outer join table2 t2 on CTE.id = t2.locationid
group by origid, CTE.description
SQL Fiddle

Creating a recursive CTE with no rootrecord

My Apologies for the appalling Title, I was trying to be descriptive but not sure I got to the point. Hopefully the below will explain it
I begin with a table that has the following information
Party Id Party Name Party Code Parent Id
1 Acme 1 ACME1 1
2 Acme 2 ACME2 1
3 Acme 3 ACME3 3
4 Acme 4 ACME4 4
5 Acme 5 ACME5 4
6 Acme 6 ACME6 6
As you can see this isn't perfect for a recursive CTE because rather than having a NULL where there isn't a parent record it is instead parented to itself (see rows 1,3 and 6). Some however are parented normally.
I have therefore tried to amend this table in a CTE then refer to the output of that CTE as part of my recursive query... This doesn't appear to be running very well (no errors yet) so I wonder if I have managed to create an infinite loop or some other error that just slows the query to a crawl rather than killing it
My Code is below... please pick it apart!
--This is my attempt to 'clean' the data and set records parented to themselves as the 'anchor'
--record
WITH Parties
AS
(Select CASE
WHEN Cur_Parent_Id = Party_Id THEN NULL
ELSE Cur_Parent_Id
END AS Act_Parent_Id
, Party_Id
, CUR_PARTY_CODE
, CUR_PARTY_NAME
FROM EDW..TBDIMD_PARTIES
WHERE CUR_FLG = 1),
--In this CTE I referred to my 'clean' records from above and then traverse through them
--looking at the actual parent record identified
linkedParties
AS
(
Select Act_Parent_Id, Party_Id, CUR_PARTY_CODE, CUR_PARTY_NAME, 0 AS LEVEL
FROM Parties
WHERE Act_Parent_Id IS NULL
UNION ALL
Select p.Act_Parent_Id, p.Party_Id, p.CUR_PARTY_CODE, p.CUR_PARTY_NAME, Level + 1
FROM Parties p
inner join
linkedParties t on p.Act_Parent_Id = t.Party_Id
)
Select *
FROM linkedParties
Order By Level
From the data I supplied earlier the results I would expect are;
Party Id Party Name Party Code Parent Id Level
1 Acme 1 ACME1 1 0
3 Acme 3 ACME3 3 0
4 Acme 4 ACME4 4 0
6 Acme 6 ACME6 6 0
2 Acme 2 ACME2 1 1
5 Acme 5 ACME5 4 1
If everything seems to be OK then I'll assume its just a processing issue and start investigating that but I am not entirely comfortable with CTE's so wish to make sure the error is not mine before looking elsewhere.
Many Thanks
I think that you made it more complicated than it needs to be :).
drop table #temp
GO
select
*
into #temp
from (
select '1','Acme 1','ACME1','1' union all
select '2','Acme 2','ACME2','1' union all
select '3','Acme 3','ACME3','3' union all
select '4','Acme 4','ACME4','4' union all
select '5','Acme 5','ACME5','4' union all
select '6','Acme 6','ACME6','6'
) x ([Party Id],[Party Name],[Party Code],[Parent Id])
GO
;with cte as (
select
*,
[Level] = 0
from #temp
where 1=1
and [Party Id]=[Parent Id] --assuming these are root records
union all
select
t.*,
[Level] = c.[Level]+1
from #temp t
join cte c
on t.[Parent Id]=c.[Party Id]
where 1=1
and t.[Party Id]<>t.[Parent Id] --prevent matching root records with themselves creating infinite recursion
)
select
*
from cte
(* should ofcourse be replaced with actual column names)

Resources