How to get anchor details when using CTE? - sql

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

Related

Wrong calculation in Sum()

hi i have 2 table and i want plus it.
Table 1 --> StudentId = 1 ,Score = 10 , 20 ,30 , StudentId = 2 ,Score = 5, 5
Table 2 --> StudentId = 1 ,Score = 5, 10 ,15 , StudentId = 2 ,Score = 15, 25
Total = StudentId = 1 ---> 90 , StudentId = 2 ---> 45
i use this query:
Select Sum(tbl_EvaPoint.Score + tbl_ActPoint.Score ),
tbl_ActPoint.StudentId
From tbl_EvaPoint
JOIN tbl_ActPoint
ON tbl_EvaPoint.StudentId = tbl_ActPoint.StudentId
GROUP BY tbl_ActPoint.StudentId`
everythings is ok but i get wrong sum instead of 90 and 45 i get 180 and 90 or somthings else.
Use UNION instead of JOIN
SELECT student_id, SUM(score)
FROM (SELECT student_id, score FROM Table1
UNION ALL
SELECT student_id, score FROM Table2
) as T
GROUP BY student_id
I think a CTE is more readable, but that is personal preference.
CREATE TABLE #S1 (
StudentID smallint
,Score smallint )
INSERT INTO #S1
SELECT 1, 10;
INSERT INTO #S1
SELECT 1, 20;
INSERT INTO #S1
SELECT 1, 30;
INSERT INTO #S1
SELECT 2, 5
INSERT INTO #S1
SELECT 2, 5;
CREATE TABLE #S2 (
StudentID smallint
,Score smallint )
INSERT INTO #S2
SELECT 1, 5;
INSERT INTO #S2
SELECT 1, 10;
INSERT INTO #S2
SELECT 1, 15;
INSERT INTO #S2
SELECT 2, 15;
INSERT INTO #S2
SELECT 2, 25;
WITH CTE AS (
SELECT
StudentID, Score
FROM #S1
UNION ALL
SELECT
StudentID, Score
FROM #S2 )
SELECT
StudentID
,SUM(Score) AS TotalScore
FROM CTE
GROUP BY StudentID

SQL Tree find anscestors at a specific level

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

get all child from an parent id

hi i need a query to do this
my table data
ID ParentID DATA
--------------------------------
1 -1 a
2 1 b
3 2 c
4 3 d
5 3 f
and what ineed a query that take a ID as parameter and return all recursively childs and Itself
parameter : (ID=2)
return must be :
ID ParentID DATA
--------------------------------
2 1 b
3 2 c
4 3 d
5 3 f
Try this:
;with temp as (
select id, parentId, data from t
where id = 2
union all
select t.id, t.parentId, t.data from t
join temp on temp.id = t.parentId
)
select * from temp
Fiddle here.
This should do it for you:
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
where topparent = 2
drop table #temp
EDIT or you can put the WHERE clause inside the first select
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
WHERE id = 2
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
drop table #temp
Results:
id parentid data
2 1 b
3 2 c
4 3 d
5 3 f
declare #ID int = 2;
with C as
(
select ID, ParentID, DATA
from YourTable
where ID = #ID
union all
select T.ID, T.ParentID, T.DATA
from YourTable as T
inner join C
on T.ParentID = C.ID
)
select ID, ParentID, DATA
from C
Try on SE-Data
try this.
select * from table where id= 2 or parentid = 2

t-sql recursive query

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

SQL Server + Select only two records for each masterID in a table

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)