CTE to traverse back up a hierarchy? - sql

I can find all the children of a given record in a hierarchical data model (see code below) but I'm not sure how to traverse back up the Parent/Child chain with a given Child ID. Can anyone point me in the right direction to figure out how to do this? Is this possible in Linq to SQL as well?
WITH TaskHierarchy (TaskID, [Subject], ParentID, HierarchyLevel, HierarchyPath) AS
(
-- Base case
SELECT
TaskID,
[Subject],
ParentID,
1 as HierarchyLevel,
CONVERT(VARCHAR(MAX),'/') AS HierarchyPath
FROM Task
WHERE TaskID = 2
UNION ALL
-- Recursive step
SELECT
t.TaskID,
t.Subject,
t.ParentID,
th.HierarchyLevel + 1 AS HierarchyLevel,
CONVERT(varchar(MAX),th.HierarchyPath + CONVERT(VARCHAR(32),t.ParentID) + '/') AS HierarchyPath
FROM Task t
INNER JOIN TaskHierarchy th ON
t.ParentID = th.TaskID
)
SELECT *
FROM TaskHierarchy
ORDER BY HierarchyLevel, [Subject]

Ah, I figured it out:
WITH Hierarchy(TaskID, [Subject], ParentID, IsProject, HLevel)
AS
(
SELECT
TaskID,
[Subject],
ParentID ,
IsProject,
0 as HLevel
FROM
Task
WHERE
TaskID = 59
UNION ALL
SELECT
SubDepartment.TaskID,
SubDepartment.[Subject],
SubDepartment.ParentID ,
SubDepartment.IsProject,
HLevel + 1
FROM
Task SubDepartment
INNER JOIN
Hierarchy ParentDepartment
ON
SubDepartment.TaskID = ParentDepartment.ParentID
)
SELECT
TaskID,
[Subject],
ParentID,
IsProject,
HLevel
FROM
Hierarchy
ORDER BY
HLevel DESC

Related

How to generate Tree path using traverse CTE

Suppose my data in table like in attached picture without having path column.
i want to generate a column, like "Path" in picture using traverse CTE in sql
Picture example:
Use a recursive CTE to solve this:
WITH recCTE
AS (
SELECT id,
parentid,
id AS original_id,
parentid AS original_parentid,
name as original_name,
1 AS depth,
CAST(name AS VARCHAR(5000)) AS path
FROM yourtable
UNION ALL
SELECT yourtable.id,
yourtable.parentid,
recCTE.original_id,
recCTE.original_parentID,
recCTE.original_name,
recCTE.depth + 1,
CAST(recCTE.path + '-' + yourtable.name as VARCHAR(5000))
FROM recCTE
INNER JOIN yourtable
ON recCTE.parentid = yourtable.id
WHERE depth < 20 /*prevent cycling*/
)
SELECT original_id as id, original_parentid as parentid, original_name as name, depth, path
FROM recCTE t1
WHERE depth = (SELECT max(depth) FROM recCTE WHERE t1.original_id = recCTE.original_id)
sqlfiddle example
That CTE has two parts:
The "Anchor Member" which is the first selection from the table. This defines the output (which columns and column type are in the output).
The "Recursive Member" which selects from the CTE in which it's contained at is performed iteratively until the join fails.
In this example we capture the path by concatenating the path to the name over and over again in the recursive member. We also track Depth (how many recursions have been performed) and track the current id and parentid as well as the original id and original parentid so they can be selected in the final SELECT statement.
try this,
;with cte(id,parentId,name,path,cnt)
AS
(
select id,parentid,name,cast(name as VARCHAR(1024)) as path, 1 as cnt from test_cte
union all
select a.id,a.parentid,a.name,CAST((a.name + '-' +path ) as VARCHAR(1024)), case when a.parentid is null then 0 else cnt + 1 end as cnt from test_cte a join cte c on c.id = a.parentid where c.cnt is not null
)
select id,parentid,name,path from (select id,parentid,name,path, row_number() over(partition by id order by cnt desc) as rank from cte) a where a.rank = 1 order by 1 asc ;

SQL Server 2012 - Convert a From-To columned table into a numbered sequence

I am trying to write a SQL query in SQL Server 2012 to convert a table that has 2 columns FROM and TO into a table that has a numerical sequence of the described route taken if you logically follow the from-to direction. I have been struggling for hours on this and any hints would be greatly appreciated. Thanks
EXAMPLE:
To answer this I'll have to assume either a sort order or a "base record". I.e. I need to know which record should be the starting point. In this example I have simply hardcoded the value of the base record.
WITH RecursiveCTE AS(
SELECT
[FROM], [TO],
1 AS SequenceNo
FROM InputTable
WHERE [FROM] = 'B' --Hardcoded value to select a base record
UNION ALL
SELECT
t.[FROM], t.[TO],
SequenceNo + 1 AS SequenceNo
FROM RecursiveCTE e
INNER JOIN InputTable t ON e.[TO] = t.[FROM]
)
--Get all records except the last one via the Recursive CTE
SELECT
[FROM] AS [Node],
SequenceNo
FROM RecursiveCTE
UNION ALL
--Get the last record in a separate query
SELECT TOP 1
[TO] AS [Node],
SequenceNo + 1 AS SequenceNo
FROM RecursiveCTE
WHERE SequenceNo = (SELECT MAX(SequenceNo) FROM RecursiveCTE)
The query uses a recursive CTE to get all records except the last one. The last record is added by the UNION ALL statement at the end.
Assuming no cycles, you can use a recursive cte:
with cte as (
select e.from, e.to, 1 as lev
from example e
where not exists (select 1 from example e2 where e2.to = e.from)
union all
select e.from, e.to, cte.lev + 1
from cte join
example e
on cte.to = e.from
)
select e.from, lev
from cte;
I assumed that you have random data on [from] or [to] column - (may be there more data in [from] column not in [to] column and vise-versa, so
if there is no problem with making an order using [from] and [to] column use the below query
with dataTest as
(
select [From], [to] , ROW_NUMBER() over(order by [From], [to]) lvl from InputTable e
)
,dataD as
(
select [From] node,(lvl*2)-1 lvl from dataTest
union all
select [To] node,(lvl*2) lvl from dataTest
)
,lastD as
(
select node, lvl, ROW_NUMBER() over(partition by node order by lvl) rn from dataD
)
select node, ROW_NUMBER() over(order by lvl) squence from lastD where rn=1 order by lvl
if your data is typically as you set in the sample you can use
with dataTest as
(
select [From], [to] ,1 lvl from InputTable e
where not exists (select 1 from InputTable e2 where e2.[to] = e.[from])
union all
select e.[from], e.[to], dataTest.lvl + 1
from dataTest join
InputTable e on dataTest.[to] = e.[from]
)
,dataD as
(
select [From] node,(lvl*2)-1 lvl from dataTest
union all
select [To] node,(lvl*2) lvl from dataTest
)
,lastD as
(
select node, lvl, ROW_NUMBER() over(partition by node order by lvl) rn
from dataD
)
select node, ROW_NUMBER() over(order by lvl) squence from lastD where rn=1 order by lvl
Hope these help you or give you an idea.

how to make a sql loop?

here is the simplified table
filesystem (id, name, parentId);
and some entries
(1, 'root', NULL)
(2, 'folder', 1)
(3, 'subfolder', 2)
(4, 'subsubfolder', 3)
is there a way using native SQL to print the absolute path of one entry ?
for instance, the last entry would print 'root/folder/subfolder/subsubfolder'. the entry 2 would print 'root/folder' and so on.
You can do something like this
with tree(id, Level, Hierarchy) as
(
select id, 0, cast(Name as varchar(max))
from filesystem
union all
select a.id, b.Level+1,
b.Hierarchy+'/'+a.Name
from filesystem a
inner join tree b on a.parentid=b.id
)
select top(1) id, Hierarchy
from tree
where id=4
order by Level desc
It will give you id with full file path.
TO read in details you can check this
You didn't state your DBMS, the following is standard (ANSI) SQL:
with recursive folder_tree as (
select id, name, parentid, name as fullpath
from filesystem
where parentid is null
union all
select c.id, c.name, c.parentid, p.fullpath||'/'||c.name
from filesystem c
join folder_tree p on c.parentid = p.id
)
select *
from folder_tree
SQLFiddle: http://sqlfiddle.com/#!15/91332/7
Recursive CTE solution for SQL Server:
WITH FileSystem(id,name,parentID)
AS
(
SELECT 1,'root',NULL
UNION ALL
SELECT 2,'folder',1
UNION ALL
SELECT 3,'subFolder',2
UNION ALL
SELECT 4,'subSubFolder',3
),
CTE_Recursion
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY ID) filePath_id,ID,CAST(name AS VARCHAR(100)) name,parentID
FROM FileSystem
WHERE parentID IS NULL
UNION ALL
SELECT A.filePath_id,B.id,CAST(A.name + '\' + B.name AS VARCHAR(100)),B.parentID
FROM CTE_Recursion A
INNER JOIN FileSystem B
ON A.ID = B.parentID
)
SELECT filePath_id,MAX(name) filePath
FROM CTE_Recursion
GROUP BY filepath_id
Results:
filePath_id filePath
-------------------- -----------------------------------
1 root\folder\subFolder\subSubFolder

SQL: How to create view from a recursive query?

Question: I have a view which I want to derive from a recursive query.
The query is of the same structure as this one here:
http://forums.asp.net/t/1207101.aspx
And represents a treeview as an ordered dataset.
How can I create a view which does this:
;WITH Tree (ID, [NAME], PARENT_ID, Depth, Sort) AS
(
SELECT ID, [NAME], PARENT_ID, 0 AS Depth, CONVERT(varchar(255), [Name]) AS Sort FROM Category
WHERE PARENT_ID = 0
UNION ALL
SELECT CT.ID, CT.[NAME], CT.PARENT_ID, Parent.Depth + 1 AS Depth,
CONVERT(varchar(255), Parent.Sort + ' | ' + CT.[NAME]) AS Sort
FROM Category CT
INNER JOIN Tree as Parent ON Parent.ID = CT.PARENT_ID
)
-- HERE IS YOUR TREE, Depths gives you the level starting with 0 and Sort is the Name based path
SELECT ID, [NAME], PARENT_ID, Depth, Sort FROM Tree
ORDER BY Sort
It should be as simple as:
CREATE VIEW YourViewName
AS
WITH Tree (ID, [NAME], PARENT_ID, Depth, Sort) AS
(
SELECT ID, [NAME], PARENT_ID, 0 AS Depth, CONVERT(varchar(255), [Name]) AS Sort
FROM Category
WHERE PARENT_ID = 0
UNION ALL
SELECT CT.ID, CT.[NAME], CT.PARENT_ID, Parent.Depth + 1 AS Depth,
CONVERT(varchar(255), Parent.Sort + ' | ' + CT.[NAME]) AS Sort
FROM Category CT
INNER JOIN Tree as Parent ON Parent.ID = CT.PARENT_ID
)
-- HERE IS YOUR TREE, Depths gives you the level starting with 0 and Sort is the Name based path
SELECT ID, [NAME], PARENT_ID, Depth, Sort FROM Tree
GO

How to find level of employee position using RECURSIVE COMMON TABLE EXPRESSION

;with Ranked(Empid,Mngrid,Empnm,RN,level) As
(select Empid,Mngrid ,Empnm ,row_number() over (order by Empid)AS RN ,
0 as level from dbo.EmpMngr),
AnchorRanked(Empid,Mngrid,Empnm,RN,level)
AS(select Empid,Mngrid,Empnm,RN ,level from Ranked ),
RecurRanked(Empid,Mngrid,Empnm,RN,level)
AS(select Empid,Mngrid,Empnm,RN,level from AnchorRanked
Union All
select Ranked.Empid,Ranked.Mngrid,Ranked.Empnm,Ranked.RN,Ranked.level + 1
from Ranked
inner join RecurRanked
on Ranked.Empid = RecurRanked.Empid AND
Ranked.RN = RecurRanked.RN+1)
select Empid,Empnm,level from RecurRanked
This may help CTE Example of a simple hierarchy
;WITH OrgChart AS
(
SELECT EmpID, EmpNm, [Level] = 0
FROM EmpMngr
WHERE MngrID IS NULL
UNION ALL
SELECT e.EmpID, e.EmpNm, [Level] + 1
FROM OrgChart o
JOIN EmpMngr e ON (e.MngrID = o.EmpID)
)
SELECT EmpID, EmpNm, [Level] FROM OrgChart