Get nested children from parent's ID with SQL Server - sql

My table is as follows:
ID Name Parent ID
1 Joe -
2 James -
3 Mike 1
4 Lewis 3
5 Anne 2
6 Lucy 4
I'd like to get the ID of the parent and all its children. For example, if I do:
Select Name from Table where ID = 1 (and nested children)
The desired output would be:
Joe
Mike
Lewis
Lucy

You can use a recursive CTE:
with cte as (
select name, id
from t
where id = 1
union all
select t.name, t.id
from cte join
t
on t.parent_id = cte.id
)
select name
from cte;

WITH CTE_Table AS (
SELECT ID, Name, Parent
FROM MyTable
UNION ALL
SELECT MyTable.ID, MyTable.Name, MyTable.Parent
FROM CTE_Table INNER JOIN
MyTable ON CTE_Table.ID = MyTable.Parent
)
SELECT Name
FROM CTE_Table
WHERE (Parent = 1)

Related

Postgres recursive CTE with Group?

I tried to find out the masterIds as Array. please find the below
create temp table location as
select 1 as id,'World' as name,null as main_id
union all
select 2,'Asia',1
union all
select 3,'Africa',1
union all
select 4,'India',2
union all
select 5,'Sri Lanka',2
union all
select 6,'Mumbai',4
union all
select 7,'South Africa',3
Table like
Id
Name
Main Id
1
World
null
2
Asia
1
3
Africa
1
4
India
2
5
Sri Lanka
2
6
Mumbai
4
7
South Africa
3
8
Cape Town
7
And My expected result mentioned below:
Id
Name
Main Id
MasterIds Arrayint
1
World
null
1
2
Asia
1
1,2
3
Africa
1
1,3
4
India
2
1,2,4
5
Sri Lanka
2
1,2,5
6
Mumbai
4
1,2,4,6
7
South Africa
3
1,3,7
8
Cape Town
7
1,3,7,8
And My code is handling only one row but i need this for entire table. please find my code below:
with recursive
cte as (
select * from location where id=4
union all
select g.* from cte join location g on g.id = cte.main_id
)
select array_agg(id) from cte l
You seem to want an array of all ancestors (including id) for each id. Here is one attempt:
with recursive cte as (
select id, name, main_id from location
union all
select cte.id, g.name, g.main_id
from cte join location g on g.id = cte.main_id
)
select id, array[id] || array_agg(main_id)
from cte l
where main_id is not null
group by id
See Fiddle
For each iteration in the recursive cte, we keep the id that we started from and traverse the tree upwards (i.e. add next ancestor to the set of ancestors). At the end we create one group for each id and aggregate all ancestors into the array.
Worth noting is that the set of ancestors is unordered, so you may end up with a "path" that is out of order. You can fix that by providing an order, here I use the level relative to the id:
with recursive
cte as (
select id, name, main_id, 0 as lvl from location
union all
select cte.id, g.name, g.main_id, lvl+1 from cte join location g on g.id = cte.main_id
)
select id, array[id] || array_agg(main_id order by lvl)
from cte l
where main_id is not null
group by id

Recursive query to retrieve all child from given id

I am migrating from oracle to postgres and I don't know how to make a recursive query
Here is an example data set:
id
id_parent
name
1
0
aa
2
0
aa
3
1
aa
4
3
aa
5
3
aa
6
2
aa
7
6
aa
For id = 3, I want to get
id
3
4
5
for id =1
id
1
3
4
5
with this I have all but I don't know how to filter by id:
SELECT id FROM (
with recursive cat as (
select * from table
union all
select table.*
from table
join cat on cat.id_parent = table.id
)
select * from cat order by id
)
as listado where id != '0' group by id
There is no need of using sub-query. You just need to fix your recursive query only as -
WITH RECURSIVE cat (id, id_parent) as
(
SELECT id, id_parent
FROM table
WHERE id = 1
UNION ALL
SELECT d.id, d.id_parent
FROM table d
JOIN cat ON cat.id = d.id_parent
)
SELECT id
FROM cat
ORDER BY id;
Demo.

join multiple times in SQL

These are the 2 tables.
Tech_data:
Id Tech Agent1_id Agent2_ID
1 JAVA 1 2
2 SQL 3 4
Agent_table
Id Name
1 Mike
2 John
3 Jim
4 Baron
I need to write a query to bring the below output
TECH_ID Tech Agent1_Name Agent2_Name
1 Java Mike John
2 SQL Jim Baron
I wrote LEFT OUTER JOIN ON tech_id=agent1_id, but i do not know how to join 2 ids in ON condition.
To prevent having to do multiple joins to the same table, you can unpivot, join and then pivot (then if you had 50 ID columns you would still only need to perform one join):
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Tech_data (Id, Tech, Agent1_id, Agent2_ID ) AS
SELECT 1, 'JAVA', 1, 2 FROM DUAL UNION ALL
SELECT 2, 'SQL', 3, 4 FROM DUAL;
CREATE TABLE Agent_table ( Id, Name ) AS
SELECT 1, 'Mike' FROM DUAL UNION ALL
SELECT 2, 'John' FROM DUAL UNION ALL
SELECT 3, 'Jim' FROM DUAL UNION ALL
SELECT 4, 'Baron' FROM DUAL;
Query 1:
SELECT *
FROM (
SELECT t.id,
t.tech,
t.num,
a.name
FROM (
SELECT *
FROM tech_data
UNPIVOT ( Agent_ID FOR num IN ( Agent1_id AS 1, Agent2_id AS 2 ) )
) t
INNER JOIN Agent_table a
ON ( t.agent_id = a.id )
)
PIVOT ( MAX( name ) FOR num IN ( 1 AS Agent1_Name, 2 AS Agent2_Name ) )
Results:
| ID | TECH | AGENT1_NAME | AGENT2_NAME |
|----|------|-------------|-------------|
| 1 | JAVA | Mike | John |
| 2 | SQL | Jim | Baron |
You can add a second left outer join just like the one you have used, on the same table by giving them different aliases as follows.
select t.Id tech_id, t.tech, a1.name, a2.name
from tech_data t
left outer join agent_table a1 on a1.Id = t.agent1_id
left outer join agent_table a2 on a2.Id = t.agent2_Id;
Check the fiddle below:
http://sqlfiddle.com/#!4/73f02b/1

How to select [ParentName] from the same table based on [ParentId]

I have a table structure like so (simplified):
Id ParentId Name Desc
------------------------------
1 NULL A
2 NULL B
3 1 A1
4 1 A2
5 2 B1
It's a big table and can be very painful to look through. So I want to create a view that will display data in slightly better way:
Id ParentId ParentName Name Desc
--------------------------------------------
1 NULL A
3 1 A A1
4 1 A A2
2 NULL B
5 2 B B1
My problem is getting that ParentName into SELECT query. I obviously (tried) can't do:
SELECT Id, ParentId, (SELECT Name FROM myTable WHERE Id = ParentId) AS ParentName, Name, Desc FROM myTable INNER JOIN .. WHERE ... GROUP BY etc.
Do I have to resort to Common Table Expression (CTE) to get this done ?
Instead of using CTE, You can consider to do that by simple self-join concept:
Select T1.Id, T1.ParentId, ParentTable.Name as ParentName, T1.Name, T1.[Desc]
From tableName T1
Left join tableName ParentTable
on T1.ParentId = ParentTable.Id
order by Name
More reference on Self-Join from Microsoft's TechNet (ref pointer credited to destination-data)
Your example has one parent level only. If there might be deeper nestings you could use a recursive CTE. In this example I added two more elements as children below children:
DECLARE #tbl TABLE(Id INT,ParentId INT, Name VARCHAR(MAX));
INSERT INTO #tbl VALUES
(1,NULL,'A')
,(2,NULL,'B')
,(3,1,'A1')
,(4,1,'A2')
,(5,2,'B1')
,(6,4,'A2a')
,(7,6,'A2a1')
;
WITH RecursiveCTE AS
(
SELECT Id,ParentId,Name,Name AS FullPath
FROM #tbl AS tbl
WHERE ParentId IS NULL
UNION ALL
SELECT a.*
,derived.FullPath + '/' + a.Name
FROM RecursiveCTE AS derived
CROSS APPLY (SELECT Id,ParentId,Name FROM #tbl AS innerTbl WHERE innerTbl.ParentId=derived.Id) AS a
)
SELECT * FROM RecursiveCTE
The result
Id ParentId Name FullPath
1 NULL A A
2 NULL B B
5 2 B1 B/B1
3 1 A1 A/A1
4 1 A2 A/A2
6 4 A2a A/A2/A2a
7 6 A2a1 A/A2/A2a/A2a1

Use a CTE to traverse to 2nd level in tree

I'm trying to use a CTE to traverse a tree in SQL Server. Ideally what I would like as output is a table which shows for each node in the tree the corresponding node that is second from the top in the tree.
I have some basic code to traverse the tree from a given node, but how can I modify it so it produces the desired output ?
DECLARE #temp TABLE
(
Id INT
, Name VARCHAR(50)
, Parent INT
)
INSERT #temp
SELECT 1,' Great GrandFather Thomas Bishop', null UNION ALL
SELECT 2,'Grand Mom Elian Thomas Wilson' , 1 UNION ALL
SELECT 3, 'Dad James Wilson',2 UNION ALL
SELECT 4, 'Uncle Michael Wilson', 2 UNION ALL
SELECT 5, 'Aunt Nancy Manor', 2 UNION ALL
SELECT 6, 'Grand Uncle Michael Bishop', 1 UNION ALL
SELECT 7, 'Brother David James Wilson',3 UNION ALL
SELECT 8, 'Sister Michelle Clark', 3 UNION ALL
SELECT 9, 'Brother Robert James Wilson', 3 UNION ALL
SELECT 10, 'Me Steve James Wilson', 3
;WITH cte AS
(
SELECT Id, Name, Parent, 1 as Depth
FROM #temp
WHERE Id = 8
UNION ALL
SELECT t2.*, Depth + 1 as 'Depth'
FROM cte t
JOIN #temp t2 ON t.Parent = t2.Id
)
SELECT *
, MAX(Depth) OVER() - Depth + 1 AS InverseDepth
FROM cte
As output I would like something like
Id Name depth2_id depth2_name
8 Sister Michelle .. 2 Grand Mom Elian ....
7 Brother David .. 2 Grand Mom Elian ....
4 Uncle Michael .. 2 Grand Mom Elian ...
Thanks for any tips or pointers.
a bit hard to get what your goal, but you can use smth like this:
;with cte AS
(
select
t.Id, t.Name, t.Parent, 1 as Depth,
null as Depth2Parent
from #temp as t
where t.Parent is null
union all
select
t.Id, t.Name, t.Parent, c.Depth + 1 as 'Depth',
isnull(c.Depth2Parent, case when c.Depth = 1 then t.Id end) as Depth2Parent
from cte as c
inner join #temp as t on t.Parent = c.Id
)
select *
from cte
sql fiddle demo