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.
Related
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
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.
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
There is a table with below structure:
ID XID RChange
1 1 1
2 2 1
3 3 1
4 1 0
5 2 0
6 3 1
ID column is an identity column
XID column will have some values repeating
RChange will have either 1 or 0
I need those rows from the table with RChange column value changing from 1 to 0 with same XID
i.e. in the above table, for XID 1 and 2 RChange value has changed from 1 to 0 but for XID 3 it has changed from 1 to 1.
So, I need to write a query which will retrieve
ID XID RChange
4 1 0
5 2 0
So, please help me with your ideas.
You have not included a timestamp so I am assuming the ID column will determine the order.
;WITH byXID AS
(
SELECT ID, XID, RChange, ROW_NUMBER() OVER(PARTITION BY XID ORDER BY ID) rn
FROM Table1
)
SELECT t1.ID, t1.XID, t1.RChange
FROM byXID t1
INNER JOIN byXID t0 ON t1.XID = t0.XID AND t0.rn = t1.rn - 1
WHERE t1.RChange = 0 AND t0.RChange = 1
SQL Fiddle demo
This is another approach:
SELECT t2.ID, t2.XID, t2.RChange
FROM Table1 t1 JOIN Table1 t2
ON t1.XID = t2.XID
WHERE t1.RChange = 1 AND t2.RChange = 0
Sql fiddle demo (Thanks #Ic. for the fiddle)
I have a table like this:
id name parent_id
1 ab1 3
2 ab2 5
3 ab3 2
4 ab4 null
5 ab5 null
6 ab6 null
I need to do a query with input id = 1 (for an example) and results will be like this:
id name parent_id
5 ab5 null
2 ab2 5
3 ab3 2
1 ab1 3
(List all parents at all level start at item id = 1)
Something like this perhaps?
WITH parents(id,name,parent,level)
AS
(
SELECT
ID,
NAME,
PARENT,
0 as level
FROM
TABLE
WHERE ID = 1
UNION ALL
SELECT
ID,
NAME,
PARENT,
Level + 1
FROM
TABLE
WHERE
id = (SELECT TOP 1 parent FROM parents order by level desc)
)
SELECT * FROM parents
Use WITH RECURSIVE. Documentation and adaptable example: Recursive Queries Using Common Table Expressions.