Say I have the following tree:
CREATE TABLE Tree(Id int, ParentId int, Name varchar(20), Level int)
INSERT INTO Tree(Id, ParentId, Name, Level)
SELECT 1, NULL, 'Bob', 0 UNION ALL
SELECT 2, 1, 'John', 1 UNION ALL
SELECT 3, 1, 'Bill', 1 UNION ALL
SELECT 4, 3, 'Peter', 2 UNION ALL
SELECT 5, 4, 'Sarah', 3
I need a script to find the ancestor of a given node at a specific level:
For example the ancestor of Sarah at level 2 is Peter, and the ancestor of Peter at level 0 is Bob.
Is there an easy way to do this using a hierarchical CTE? sqlfiddle
You can do this:
;WITH TreeCTE
AS
(
SELECT Id, ParentId, Name, Level
FROM Tree
WHERE Id = 5
UNION ALL
SELECT t.Id, t.ParentId, t.Name, t.Level
FROM TreeCTE AS c
INNER JOIN Tree t ON c.ParentId = t.Id
)
SELECT * FROM TreeCTE;
This will give you the parent-child chain beginning from Sarah with the id = 5 ending with the top most parent with the parentid = NULL.
Here is the updated sql fiddle
declare #fromid int, #level int
select #fromid = 5, #level = 2 -- for sarah's ancestor at level 2
select #fromid = 4, #level = 0 -- for peter's ancestor at level 0
;with cte as (
select * from tree where id = #fromId
union all
select t.Id, t.ParentId, t.Name, t.Level from cte
inner join tree t on t.id = cte.ParentId
)
select * from cte
where Level=#level
try this:
DEclare #level int=0
Declare #name varchar(10)='Peter'
;WITH CTE as (
select * from tree where Name=#name
UNION ALL
select t.* from Tree t inner join CTE c on
t.ID=c.ParentId and t.Level >=#level)
select top 1 Name as Ancestor from CTE order by id
Related
I have a table with 2 columns (id, childId) with the following data:
1, 2
2, 4
3, 5
4, 6
5, null
6, null
I have the following CTE that gets the records and it works fine.
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId
FROM #id
WHERE id = 1
UNION ALL
SELECT b.id, b.childId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
However, I would like to add the anchor details so that the results will look like:
1, 2, null
2, 4, 1
4, 6, 1
6, null, 1
So that that 3rd column is the main anchor record.
Is this possible?
Just add a thrid column in CTE.
declare #mytable table ( id int, childId int );
insert #mytable
( id, childId )
values ( 1, 2 ),
( 2, 4 ),
( 3, 5 ),
( 4, 6 ),
( 5, null ),
( 6, null );
declare #id table ( id int );
insert into #id
select 1;
insert into #id
select 3;
with cte
as ( select id ,
childId ,
id root
from #mytable
where id in ( select id
from #id )
union all
select b.id ,
b.childId ,
cte.root
from #mytable b
inner join cte on b.id = cte.childId
)
select *
from cte;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, case
WHEN id = anchorId THEN NULL
ELSE anchorId
END as anchorId
FROM cte
With test code provided:
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM #id
WHERE id IN (1,3)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, CASE
WHEN id = anchorId THEN NULL
ELSE anchorId
END AS anchorId
FROM cte
i have a temporary table in which i have the following data , i want to filter the rows of child with his parent categoryID untill its reaches at the top of that Parent in those hierarchy .
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct from #newtemp where ParentCategoryId!=0
union all
select cte.rowid,cte.ParentCategoryID,cte.CategoryID,cte.Status,cte.Level,cte.CategoryName,cte.ISProduct
from #newtemp inner join cte ON cte.CategoryId=#newtemp.ParentCategoryId
)
select * from cte
You need replace cte.CategoryId=#newtemp.ParentCategoryId on c.ParentCategoryId = #newtemp.CategoryID
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid, ParentCategoryID, CategoryID, Status, Level, CategoryName, ISProduct
from #newtemp
where ParentCategoryId!=0
union all
select t.rowid, t.ParentCategoryID, t.CategoryID, t.Status, t.Level, t.CategoryName, t.ISProduct
from #newtemp t inner join cte c ON c.ParentCategoryId = t.CategoryID
)
select * from cte
Demo on SQLFiddle
If I understand you correct. You what something like this:
First some test data:
DECLARE #tbl TABLE
(
rowid INT,
parentCategoryID INT,
CategoryID INT,
[Status] INT,
[Level] INT,
CategoryName VARCHAR(100),
ISProduct BIT
)
INSERT INTO #tbl
SELECT 1,0,1,1,0,'jewellary',1 UNION ALL
SELECT 2,0,2,1,0,'f',0 UNION ALL
SELECT 11,2,4,1,10,'ghdf',1
Then the CTE like this:
;WITH cte_name (rowid,CategoryID,parentCategoryID,HasChildren)
AS
(
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
CASE WHEN EXISTS
(
SELECT
NULL
FROM
#tbl AS tblInner
WHERE
tblInner.parentCategoryID=tbl.CategoryID
)
THEN 1
ELSE 0
END
AS HasChildren
FROM
#tbl AS tbl
WHERE
tbl.parentCategoryID=0
UNION ALL
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
cte.HasChildren
FROM
#tbl AS tbl
JOIN cte_name AS cte
on cte.CategoryID=tbl.parentCategoryID
)
SELECT
tbl.*
FROM
cte_name
JOIN #tbl AS tbl
ON cte_name.rowid=tbl.rowid
WHERE
cte_name.HasChildren=1
I have an issue I just can't get my head around. I know what I want, just simply can't get it out on the screen.
What I have is a table looking like this:
Id, PK UniqueIdentifier, NotNull
Name, nvarchar(255), NotNull
ParentId, UniqueIdentifier, Null
ParentId have a FK to Id.
What I want to accomplish is to get a flat list of all the id's below the Id I pass in.
example:
1 TestName1 NULL
2 TestName2 1
3 TestName3 2
4 TestName4 NULL
5 TestName5 1
The tree would look like this:
-1
-> -2
-> -3
-> -5
-4
If I now ask for 4, I would only get 4 back, but if I ask for 1 I would get 1, 2, 3 and 5.
If I ask for 2, I would get 2 and 3 and so on.
Is there anyone who can point me in the right direction. My brain is fried so I appreciate all help I can get.
declare #T table(
Id int primary key,
Name nvarchar(255) not null,
ParentId int)
insert into #T values
(1, 'TestName1', NULL),
(2, 'TestName2', 1),
(3, 'TestName3', 2),
(4, 'TestName4', NULL),
(5, 'TestName5', 1)
declare #Id int = 1
;with cte as
(
select T.*
from #T as T
where T.Id = #Id
union all
select T.*
from #T as T
inner join cte as C
on T.ParentId = C.Id
)
select *
from cte
Result
Id Name ParentId
----------- -------------------- -----------
1 TestName1 NULL
2 TestName2 1
5 TestName5 1
3 TestName3 2
Here's a working example:
declare #t table (id int, name nvarchar(255), ParentID int)
insert #t values
(1, 'TestName1', NULL),
(2, 'TestName2', 1 ),
(3, 'TestName3', 2 ),
(4, 'TestName4', NULL),
(5, 'TestName5', 1 );
; with rec as
(
select t.name
, t.id as baseid
, t.id
, t.parentid
from #t t
union all
select t.name
, r.baseid
, t.id
, t.parentid
from rec r
join #t t
on t.ParentID = r.id
)
select *
from rec
where baseid = 1
You can filter on baseid, which contains the start of the tree you're querying for.
Try this:
WITH RecQry AS
(
SELECT *
FROM MyTable
UNION ALL
SELECT a.*
FROM MyTable a INNER JOIN RecQry b
ON a.ParentID = b.Id
)
SELECT *
FROM RecQry
Here is a good article about Hierarchy ID models. It goes right from the start of the data right through to the query designs.
Also, you could use a Recursive Query using a Common Table Expression.
I'm guessing that the easiest way to accomplish what you're looking for would be to write a recursive query using a Common Table Expression:
MSDN - Recursive Queries Using Common Table Expressions
Based on an existing table I used CTE recursive query to come up with following data. But failing to apply it a level further.
Data is as below
id name parentid
--------------------------
1 project 0
2 structure 1
3 path_1 2
4 path_2 2
5 path_3 2
6 path_4 3
7 path_5 4
8 path_6 5
I want to recursively form full paths from the above data. Means the recursion will give the following output.
FullPaths
-------------
Project
Project\Structure
Project\Structure\Path_1
Project\Structure\Path_2
Project\Structure\Path_3
Project\Structure\Path_1\path_4
Project\Structure\Path_2\path_5
Project\Structure\Path_3\path_6
Thanks
Here's an example CTE to do that:
declare #t table (id int, name varchar(max), parentid int)
insert into #t select 1, 'project' , 0
union all select 2, 'structure' , 1
union all select 3, 'path_1' , 2
union all select 4, 'path_2' , 2
union all select 5, 'path_3' , 2
union all select 6, 'path_4' , 3
union all select 7, 'path_5' , 4
union all select 8, 'path_6' , 5
; with CteAlias as (
select id, name, parentid
from #t t
where t.parentid = 0
union all
select t.id, parent.name + '\' + t.name, t.parentid
from #t t
inner join CteAlias parent on t.parentid = parent.id
)
select *
from CteAlias
Try something like this:
WITH Recursive AS
(
SELECT
ID,
CAST(PathName AS VARCHAR(500)) AS 'FullPaths',
1 AS 'Level'
FROM
dbo.YourTable
WHERE
ParentID = 0
UNION ALL
SELECT
tbl.ID,
CAST(r.FullPaths + '\' + tbl.PathName AS VARCHAR(500)) AS 'FullPaths',
r.Level + 1 AS 'Level'
FROM
dbo.YourTable tbl
INNER JOIN
Recursive r ON tbl.ParentID = r.ID
)
SELECT * FROM Recursive
ORDER BY Level, ID
Output:
ID FullPaths Level
1 project 1
2 project\structure 2
3 project\structure\path_1 3
4 project\structure\path_2 3
5 project\structure\path_3 3
6 project\structure\path_1\path_4 4
7 project\structure\path_2\path_5 4
8 project\structure\path_3\path_6 4
try this:
DECLARE #YourTable table (id int, nameof varchar(25), parentid int)
INSERT #YourTable VALUES (1,'project',0)
INSERT #YourTable VALUES (2,'structure',1)
INSERT #YourTable VALUES (3,'path_1',2)
INSERT #YourTable VALUES (4,'path_2',2)
INSERT #YourTable VALUES (5,'path_3',2)
INSERT #YourTable VALUES (6,'path_4',3)
INSERT #YourTable VALUES (7,'path_5',4)
INSERT #YourTable VALUES (8,'path_6',5)
;WITH Rec AS
(
SELECT
CONVERT(varchar(max),nameof) as nameof,id
FROM #YourTable
WHERE parentid=0
UNION ALL
SELECT
CONVERT(varchar(max),r.nameof+'\'+y.nameof), y.id
FROM #yourTable y
INNER jOIN Rec r ON y.parentid=r.id
)
select * from rec
output:
nameof
-----------------------------------------------
project
project\structure
project\structure\path_1
project\structure\path_2
project\structure\path_3
project\structure\path_3\path_6
project\structure\path_2\path_5
project\structure\path_1\path_4
(8 row(s) affected)
Something like
;WITH MyCTE AS
(
SELECT
name AS FullPaths, id
FROM
MyTable
WHERE
parentid = 0 /*Normally it'd be IS NULL with an FK linking the 2 columns*/
UNION ALL
SELECT
C.FullPaths + '\' + M.name, M.id
FROM
MyCTE C
JOIN
MyTable M ON M.parentid = C.id
)
SELECT FullPaths FROM MyCTE
You'll have to change the name of #test table I was using.
WITH cte(id, name, parentid) AS
(
SELECT id, convert(varchar(128), name), parentid
FROM #test
WHERE parentid = 0
UNION ALL
SELECT t.id, convert(varchar(128), c.name +'\'+t.name), t.parentid
FROM #test t
INNER JOIN cte c
ON c.id = t.parentid
)
SELECT name as FullPaths
FROM cte
order by id
I got a child table that contains 1 to n records linked to the master table via a MasterID column.
How can I select from the child table only the first 5 records for each MasterID?
Using Sql Server CTE and ROW_NUMBER you could try using
DECLARE #ParentTable TABLE(
ID INT
)
INSERT INTO #ParentTable SELECT 1
INSERT INTO #ParentTable SELECT 2
INSERT INTO #ParentTable SELECT 3
DECLARE #ChildTable TABLE(
ID INT,
ParentID INT
)
INSERT INTO #ChildTable SELECT 1, 1
INSERT INTO #ChildTable SELECT 2, 1
INSERT INTO #ChildTable SELECT 3, 1
INSERT INTO #ChildTable SELECT 4, 1
INSERT INTO #ChildTable SELECT 5, 1
INSERT INTO #ChildTable SELECT 6, 1
INSERT INTO #ChildTable SELECT 7, 1
INSERT INTO #ChildTable SELECT 8, 2
INSERT INTO #ChildTable SELECT 9, 2
INSERT INTO #ChildTable SELECT 10, 3
INSERT INTO #ChildTable SELECT 11, 3
;WITH RowNums AS(
SELECT pt.ID ParentID,
ct.ID ChildID,
ROW_NUMBER() OVER (PARTITION BY pt.ID ORDER BY ct.ID) RowNum
FROM #ParentTable pt INNER JOIN
#ChildTable ct ON pt.ID = ct.ParentID
)
SELECT ParentID,
ChildID
FROM RowNums
WHERE RowNum <= 5
Try a regular join where the constraint is a subquery pulling the TOP 5 from the child table. In untested pseudocode:
SELECT A.MasterID, B.*
FROM MasterTable A
JOIN ChildTable B
ON A.MasterID = B.MasterID
AND B.ChildID IN (SELECT Top 5 ChildID FROM ChildTable
WHERE MasterID = A.MasterID ORDER BY Whatever)