SQL recursive hierarchy - sql
I am struggling to get one recursive CTE to work as desired but still with no chance..
So, I have the following similar table structures:
tblMapping:
map_id | type_id | name | parent_id
1 1 A1 0
2 1 A2 0
3 1 A3 1
4 1 A4 3
5 2 B1 0
6 2 B2 5
7 2 B3 6
8 1 A5 4
9 2 B4 0
tblRoleGroup:
role_group_id | type_id | map_id | desc_id
1 1 0 null
1 2 0 null
2 1 3 1
2 2 6 0
3 1 8 1
3 2 9 1
In tblRoleGroup, the desc_id field means:
null - allow all (used only in combination with map_id=0)
0 - allow all from parent including parent
1 - allow only current node
Still in tblRoleGroup if map_id=0 then the query should get all elements from same type_id
The query result should look like this:
role_group_id | type_id | map_id | path
1 1 1 A1
1 1 2 A2
1 1 3 A1.A3
1 1 4 A1.A3.A4
1 1 8 A1.A3.A4.A5
1 2 5 B1
1 2 6 B1.B2
1 2 7 B1.B2.B3
1 2 9 B4
2 1 3 A1.A3
2 2 6 B1.B2
2 2 7 B1.B2.B3
3 1 8 A1.A3.A4.A5
3 2 9 B4
The query below solves only a part of the expected result, but I wasn't able to make it work as the expected result..
WITH Hierarchy(map_id, type_id, name, Path) AS
(
SELECT t.map_id, t.type_id, t.name, CAST(t.name AS varchar(MAX)) AS Expr1
FROM dbo.tblMapping AS t
LEFT JOIN dbo.tblMapping AS t1 ON t1.map_id = t.parent_id
WHERE (t1.parent_id=0)
UNION ALL
SELECT t.map_id, t.type_id, t.name, CAST(h.Path + '.' + t.name AS varchar(MAX)) AS Expr1
FROM Hierarchy AS h
JOIN dbo.tblMapping AS t ON t.parent_id = h.map_id
)
SELECT h.map_id, h.type_id, t.role_group_id, h.Path AS Path
FROM Hierarchy AS h
LEFT JOIN dbo.tblRoleGroup t ON t.map_id = h.map_id
Could someone help me on this?
Thank you
At first I create a function that brings all descendants of passed map_id:
CREATE FUNCTION mapping (#map_id int)
RETURNS TABLE
AS
RETURN
(
WITH rec AS (
SELECT map_id,
[type_id],
CAST(name as nvarchar(max)) as name,
parent_id
FROM tblMapping
WHERE map_id = #map_id
UNION ALL
SELECT m.map_id,
m.[type_id],
r.name+'.'+m.name,
m.parent_id
FROM rec r
INNER JOIN tblMapping m
ON m.parent_id = r.map_id
)
SELECT *
FROM rec
);
GO
Then run this:
;WITH rec AS (
SELECT map_id,
[type_id],
CAST(name as nvarchar(max)) as name,
parent_id
FROM tblMapping
WHERE parent_id=0
UNION ALL
SELECT m.map_id,
m.[type_id],
r.name+'.'+m.name,
m.parent_id
FROM rec r
INNER JOIN tblMapping m
ON m.parent_id = r.map_id
)
SELECT t.role_group_id,
r.[type_id],
r.map_id,
r.name as [path]
FROM tblRoleGroup t
CROSS JOIN rec r
WHERE r.[type_id] = CASE WHEN t.desc_id IS NULL AND t.map_id = 0 THEN t.[type_id] ELSE NULL END
OR r.map_id = CASE WHEN t.desc_id = 1 THEN t.map_id ELSE NULL END
OR r.map_id IN (
SELECT map_id
FROM dbo.mapping (CASE WHEN t.desc_id = 0 THEN t.map_id ELSE NULL END)
)
ORDER BY role_group_id, r.[type_id], r.map_id
Will give you:
role_group_id type_id map_id path
1 1 1 A1
1 1 2 A2
1 1 3 A1.A3
1 1 4 A1.A3.A4
1 1 8 A1.A3.A4.A5
1 2 5 B1
1 2 6 B1.B2
1 2 7 B1.B2.B3
1 2 9 B4
2 1 3 A1.A3
2 2 6 B1.B2
2 2 7 B1.B2.B3
3 1 8 A1.A3.A4.A5
3 2 9 B4
Related
SQL: subset data: select id when time_id for id satisfy a condition from another column
I have a data (dt) in SQL like the following: ID time_id act rd 11 1 1 1 11 2 4 1 11 3 7 0 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 13 1 4 1 13 2 1 0 15 1 3 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following ID time_id act rd 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt: SELECT * FROM dt GROUP BY ID Having min(time_id) == 5;
This query: select id from tablename where time_id = 5 returns all the ids that you want in the results. Use it with the operator IN: select * from tablename where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists: select t.* from t where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS ( SELECT id FROM tab WHERE time_id = 5 ) SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;
SQL - Query to identify the parentid in a hierarchy with some conditions
I have a table in SQL azure database with a hierarchy of parents and i want to identify in the total hierarchy of the parents the parent id for each item that meets some conditions. As an example this could be a great example categoryId parentId Typeid 1 null 1 2 1 2 3 2 3 4 3 4 5 3 4 6 null 5 7 6 1 8 7 2 9 8 3 10 9 4 11 9 4 And I want to find for each categoryID the ParentId in the hierarchy that the type is 1 and if this category is the type 1 put the same category, in order to receive something like this. categoryId parentId Typeid ParentSearch 1 null 1 1 2 1 2 1 3 2 3 1 4 3 4 1 5 3 4 1 6 null 5 null 7 6 1 7 8 7 2 7 9 8 3 7 10 9 4 7 11 9 4 7 As you can see all the categories from 1 to 5 the parent with type 1 is the category 1 and for the categories from 6 to 11, the 6 need to be null and the rest the parent is 7. It could be possible? Maybe creating a path or something. i have done doing several left joins at leves and it is fine but i dont want to create 500leves in order to be sure that we never have 500 levels childs. Thanks and regards!
A recursive CTE is your friend: WITH parents AS (SELECT categoryid, parentid, typeid , CASE WHEN typeid = 1 THEN categoryid ELSE NULL END AS parentsearch FROM mytable WHERE parentid IS NULL UNION ALL SELECT c.categoryid, c.parentid, c.typeid , coalesce(p.parentsearch, CASE WHEN c.typeid = 1 THEN c.categoryid ELSE null END) FROM mytable AS c JOIN parents AS p ON c.parentid = p.categoryid) SELECT * FROM parents ORDER BY categoryid; categoryid parentid typeid parentsearch ---------- ---------- ---------- ------------ 1 (null) 1 1 2 1 2 1 3 2 3 1 4 3 4 1 5 3 4 1 6 (null) 5 (null) 7 6 1 7 8 7 2 7 9 8 3 7 10 9 4 7 11 9 4 7 Sql Server db<>fiddle example.
Another solution that uses a recursive CTE. But this one seeds from the childs. WITH RCTE AS ( SELECT t.categoryId, t.parentId, t.typeId, 0 as lvl, t.categoryId as nextCategoryId, t.parentId as nextParentId, t.typeId as nextTypeId FROM CategoryRelations t UNION ALL SELECT c.categoryId, c.parentId, c.typeId, c.lvl + 1, t.categoryId, t.parentId, t.typeId FROM RCTE c JOIN CategoryRelations t ON t.categoryId = c.nextParentId WHERE c.typeId != 1 ) SELECT c.categoryId, c.parentId, c.typeId, (case when c.nextTypeId = 1 then c.nextCategoryId end) as ParentSearch FROM RCTE c WHERE (c.nextTypeId = 1 or c.parentId is null) ORDER BY categoryId; A test on rextester here
SQL Recursive Query on hirachical name
New to MS SQL. Having the table like as follows, ID Name Parent ID 1 AA 0 2 BB 1 3 CC 2 4 DD 3 5 EE 3 6 FF 4 7 FG 6 8 AD 1 9 AC 2 My expected Result is showed in the Hierarchical Name ID Name Parent ID Hirarchical Name 1 AA 0 AA 2 BB 1 AA.BB 3 CC 2 AA.BB.CC 4 DD 3 AA.BB.CC.DD 5 EE 3 AA.BB.CC.EE 6 FF 4 AA.BB.CC.DD.FF 7 FG 6 AA.BB.CC.DD.FF.FG 8 AD 1 AA.AD 9 AC 2 AA.BB.AC Thanks for your help in advance.
Recursive CTEs are a bit tricky. This does what you want: with cte as ( select id, name, convert(varchar(max), name) as hier, 1 as lev from t where parentid = 0 union all select t.id, t.name, cte.hier + '.' + t.name, lev + 1 from cte join t on t.parentid = cte.id where lev < 10 ) select * from cte; Here is a db<>fiddle.
SQL Recursive CTE unexpectedly returns alternating sets
I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios. Below are the results of the code I'm using at the bottom. Scenario RowNumber Value Vals 1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C 1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C 1 9 A A,A,A,A,A,A,A,A,A,A,B,C 1 10 A A,A,A,A,A,A,A,A,A,B,C 1 11 A A,A,A,A,A,A,A,A,B,C 1 12 A A,A,A,A,A,A,A,B,C 1 13 A A,A,A,A,A,A,B,C 1 14 A A,A,A,A,A,B,C 1 15 A A,A,A,A,B,C 1 16 A A,A,A,B,C 1 17 A A,A,B,C 1 18 A A,B,C 1 19 A B,C 1 20 B C 1 21 C 2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C 2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C 2 8 A A,A,A,A,A,A,A,A,A,B,B,C 2 9 A A,A,A,A,A,A,A,A,B,B,C 2 10 A A,A,A,A,A,A,A,B,B,C 2 11 A A,A,A,A,A,A,B,B,C 2 12 A A,A,A,A,A,B,B,C 2 13 A A,A,A,A,B,B,C 2 14 A A,A,A,B,B,C 2 15 A A,A,B,B,C 2 16 A A,B,B,C 2 17 A B,B,C 2 18 B B,C 2 19 B C 2 20 C 2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C 3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C 3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C 3 9 A A,A,A,A,A,A,A,A,A,B,C,C 3 10 A A,A,A,A,A,A,A,A,B,C,C 3 11 A A,A,A,A,A,A,A,B,C,C 3 12 A A,A,A,A,A,A,B,C,C 3 13 A A,A,A,A,A,B,C,C 3 14 A A,A,A,A,B,C,C 3 15 A A,A,A,B,C,C 3 16 A A,A,B,C,C 3 17 A A,B,C,C 3 18 A B,C,C 3 19 B C,C 3 20 C C 3 21 C 4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C 4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C 4 8 A A,A,A,A,A,A,A,A,B,B,B,C 4 9 A A,A,A,A,A,A,A,B,B,B,C 4 10 A A,A,A,A,A,A,B,B,B,C 4 11 A A,A,A,A,A,B,B,B,C 4 12 A A,A,A,A,B,B,B,C 4 13 A A,A,A,B,B,B,C 4 14 A A,A,B,B,B,C 4 15 A A,B,B,B,C 4 16 A B,B,B,C 4 17 B B,B,C 4 18 B B,C 4 19 B C 4 20 C This is the code I used to generate the above sample. Where am I going wrong? CREATE TABLE #temp3 ( Scenario INT ,Vals VARCHAR(64) ,LEN INT ) ; WITH vals AS ( SELECT v.* FROM (VALUES ('A'), ('B'), ('C')) v(x) ), CTE AS ( SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN UNION ALL SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1 FROM CTE JOIN vals ON vals.x >= RIGHT(CTE.STR, 1) WHERE CTE.LEN < 19 ) INSERT INTO #temp3 SELECT ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario ,STR + ',C' AS Vals ,LEN FROM CTE WHERE STR + 'C' LIKE '%B%' AND LEN = 19 ; -- Split strings created above into individual characters WITH cte(Scenario, Value, Vals) AS ( SELECT Scenario ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals FROM #temp3 UNION ALL SELECT Scenario ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') FROM cte WHERE Vals > '' ) SELECT Scenario ,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber ,Value ,Vals FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition. When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like WITH cte(Scenario, depth, Value, Vals) AS ( SELECT Scenario, 0 depth ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals FROM #temp3 UNION ALL SELECT Scenario, depth+1 ,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) ,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') FROM cte WHERE Vals > '' ) SELECT Scenario ,depth ,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber ,Value ,Vals FROM cte t
SQL Server CTE query not getting desired result
I have table with few columns & I am using a CTE to get data in a hierarchical order in sorted manner: Cat 1 --item X1 --item X2 Cat 2 Cat 3 Cat 4 cat 5 --item A1 --item A2 Cat 6 Table data: ID LanguageID Name ParentID Active ---------------------------------------------- 1 1 Cat 1 0 1 2 1 item X1 1 1 3 1 item X2 1 1 4 1 cat 2 0 1 5 1 cat 3 0 0 6 1 cat 4 0 1 7 1 cat 5 0 1 8 1 item A1 7 1 9 1 item A2 7 0 10 1 cat 6 0 1 CTE query: WITH CTE (ID, ParentID, Name, LanguageID, Active, Depth, SortCol) AS ( SELECT ID, ParentID, Name, LanguageID, Active, 0, CAST(ID AS varbinary(max)) FROM Project WHERE ParentID = 0 AND Active = 1 UNION ALL SELECT d.ID, d.ParentID, d.Name, p.LanguageID, p.Active, p.Depth + 1, CAST(SortCol + CAST(d.ID AS binary(4)) AS varbinary(max)) FROM Project AS d JOIN CTE AS p ON d.ParentID = p.ID WHERE p.Active = 1 ) SELECT ID, ParentID, Name, LanguageID, Active, Depth, REPLICATE(' ', Depth) + Name as HName FROM CTE WHERE LanguageID = 1 AND Active = 1 ORDER BY Name Above query hides the parent category but not the child items. Second I am only able to order parent category in name but not the child items. Expected output should be: Cat 1 --item X1 --item X2 Cat 2 Cat 4 cat 5 --item A1 Cat 6 UPDATE: Just to mention I am not showing the all the columns of CTE query in above output below is the complete output of query ID ParentID Name LanguageID Active Depth HName ------------------------------------------------------------------ 1 0 Cat 1 1 1 0 Cat 1 4 1 item X1 1 1 0 item X1 7 1 item X2 1 1 0 item X2 8 0 cat 2 1 1 1 cat 2 9 0 cat 4 1 1 1 cat 4 10 0 cat 5 1 1 0 cat 5 3 7 item A1 1 1 1 item A1 2 7 item A2 1 1 1 item A2 6 0 cat 6 1 1 0 cat 6
you can use self join here. see the below query select p.Name, c.Name,c.Active,c.Id,c.LanguageID From Project c left outer join Project p where c.ParentId = p.Id