Simple Recursive Query child to last parent - sql

I need to retrieve the last (or first) id of a child's parent.
Example:
ID PARENT_ID
----------------
1 NULL
2 1
3 2
So if I search the parent id of id=3 I would have 1 as result.
I tried this but it gives me the same id...
with
tree(id)
as
(
select id
from myTable
where id = 3
union all
select t.id
from myTable t
inner join tree on tree.id = t.father_id
)
select *
from tree;
I already saw examples here and on several websites ;)

You've got some inconsistent naming here. But anyway, your CTE needs to include the parent_id too.
Like this:
with
tree(id,parent_id)
as
(
select id, parent_id
from myTable
where id = 3
union all
select t.id, t.parent_id
from myTable t
inner join tree on t.id = tree.parent_id
)
select *
from tree;

Related

select node-ancestors recursively

Suppose I have the following tree:
The table that stores the data is presented below accordingly:
node_id
parent_id
A
null
B
A
C
A
D
B
I want to select all node ancestors for every node in the tree.
the root node has no ancestors so it'll not show up.
Example of what I want -
node_id
ancestor_id
B
A
C
A
D
B
D
A
Glad for any help :)
I am working with pgsql.
You need a recursive query :
WITH RECURSIVE cte AS (
SELECT node_id, parent_id AS ancestor_id
FROM your_table
WHERE parent_id is not null
UNION ALL
SELECT children.node_id, parent.parent_id
FROM cte AS chidren
INNER JOIN your_table AS parent
ON parent.node_id = children.parent_id
)
SELECT *
FROM cte ;
This query may not stop if you have a loop in the node_id => paren_id tree. In order to avoid infinite loops in the recursive query, you have to add a test :
WITH RECURSIVE cte AS (
SELECT node_id, parent_id AS ancestor_id, array[node_id, parent_id] AS check
FROM your_table
WHERE parent_id is not null
UNION ALL
SELECT children.node_id, parent.parent_id, children.check || parent.parent_id
FROM cte AS chidren
INNER JOIN your_table AS parent
ON parent.node_id = children.parent_id
WHERE NOT children.check #> array[parent.parent_id] -- this condition will avoid infinite loops
)
SELECT node_id, ancestor_id
FROM cte ;

SQL - return a value referenced by initial value from same table

I'm sure this has been asked before, but I can't seem to find any questions that help.
Here's an example of a table:
ID Name Parent ID
---------------------------
1 Ice cream 3
2 Chips 4
3 Sweets null
I'm trying to figure out how to write a single query which, given ID=1, will return me rows 1 and 3. Is this possible without making two queries?
Also, is there a way to return the information of the parent as a custom column? So, rather than returning 2 rows, returning the row where id=1 with parent_id=3 added on?
You can use union all and exists:
select * from mytable where parent_id = 3
union all
select t.*
from mytable t
where exists (select 1 from mytable t1 where t1.parent_id = t.id and t.parent_id = 3)
If you want to do this over multiple levels of hierarchy, then you would typically use a recursive query. The syntax slightly varies accross databases (and not all of them support recursion), but the idea is:
with recursive cte as (
select * from mytable where parent_id = 3
union all
select t.*
from cte c
inner join mytable t on t.parent_id = c.id
)
select * from cte
This is how a straight up noob would do it. Hold tight for someone to give a better way
SELECT ID, Name, Parent_ID
FROM table
WHERE ID = 1
UNION
SELECT ID, Name, Parent_ID
FROM table
WHERE ID = (SELECT Parent_ID FROM table WHERE ID = 1)
Are you looking for something like this?:
select child.ID, child.Name, parent.ID as ParentId, parent.Name as ParentName
from T child left outer join T parent on parent.Id = child.parent_id;

union and select

I have a table in the following structure. The element with p_id as null is to be displayed as root and all elements mentioned in p_id column as inner and rest of the elements in the id column as leaf. is this the correct way to write the query or is there a more optimized way to write query in postgres sql.
select id, 'Leaf' as node
from tree
where id NOT IN ( select distinct t1.id as node
from tree t1 join tree t2 on t1.id = t2.p_id )
union
select distinct p_id, 'Inner' as node
from tree
where p_id NOT IN( select id from tree where p_id IS NULL)
union
select id, 'Root'
from tree
where p_id is NULL
order by id
The table for the same looks like
I have tried above query and it gives expected result, however, I am not sure if this perfect.
id p_id
-----------
1 2
3 2
6 8
9 8
2 5
8 5
5 (null)
1 Leaf
2 Inner
3 Leaf
5 Root
6 Leaf
8 Inner
9 Leaf
Usually when working with trees, there is a separate table of nodes. If so, you don't have to use union for this but can move the logic into the select:
select id,
(case when not exists (select 1 from tree t where t.id = n.id and t.p_id is null)
then 'root'
when not exists (select 1 from tree t where t.p_id = n.id)
then 'leaf'
else 'inner'
end)
from nodes n;
You can also do this without the nodes table:
select v.id,
(case when count(*) filter (where t.p_id is null) > 0 then 'root'
when bool_and(v.is_child) then 'leaf'
else 'inner'
end)
from tree t cross join lateral
(values (id, true), (p_id, false)
) v(id, is_child)
where v.id is not null
group by v.id
order by v.id;
Here is a db<>fiddle.

Tricky SQL query to get all unmatched record

I need a tricky SQL query that should lists to me all unmatched destinations. But there is parent-child relation in destination table. So if a matched destination has an unmatched child destination both parent and child should be listed. I tried to create a table to visualize it. I hope I would help to understand better way.
I tried to write below query bot it did not work
SELECT
*
FROM
Table_A AS ParentTable
WHERE
ID NOT IN (SELECT TableA_ID FROM Table_B WHERE TableA_ID IS NOT NULL)
-- this is to find UNmatched records
AND NOT EXISTS (
SELECT * FROM Table_A AS ChildTable
WHERE ChildTable.Parent = ParentTable.Code)
-- this is the part that I was not sure (:
Here is main destination table
TABLE A
ID Destination ParentID
1 France 0
2 Île-De-France 1
3 Ablis 2
4 Provence-Alpes-Cote D'azur 1
5 Aix-En-Provence 4
Here is the second table
TABLE B
ID Destination TableA_ID
100 France 1
101 Île-De-France 2
102 Ablis NULL
103 Provence-Alpes-Cote D'azur 4
104 Aix-En-Provence 5
In this situation I need to retrieve below table since "Ablis" not matched.
RESULTING TABLE
ID Destination ParentID
1 France 0
2 Île-De-France 1
3 Ablis 2
You can try a recursive cte like below.
See live demo
; with rcte as
(
SELECT
A.id, A.destination, A.parentid
FROM
TABLE_B b join TABLE_A a on a.destination=b.destination WHERE TableA_ID IS NULL
union all
select
T.id, T.destination, T.parentid
from
table_A T join rcte r
on r.parentid=t.id
)
select * from rcte order by parentid asc
I would use not exist with recursive CTE
with t as (
select Id, Destination, ParentID
from table_a a
where not exists (select 1 from table_b where a.id = TableA_ID)
union all
select t.Id, t.Destination, t.ParentID
from t c join table_a a1
on t.id = c.ParentID
)
select * from t
order by 3;

Table self related query

I have a table which has self relationship:
id -- parentId columns
I need a query to get the parent row where the parentId is null, but I couldn't figure out the proper way of doing this.
select * from table1 where id = parentId;
Apparently this is not working, it will just give the direct parent.
Any help
"Get the parent row where the parentId is null" makes no sense, but in case you actually meant "get the parent row until the parentId is null", then this recursive CTE should do the trick:
WITH cte AS (
SELECT * FROM table1 WHERE id = 7
UNION ALL
SELECT table1.* FROM table1 JOIN cte ON table1.id = cte.parentId
)
SELECT * FROM cte
This returns the row with id = 7 and all its ancestors recursively. Replace 7 according to your needs.
SELECT *
FROM table1 AS A
LEFT JOIN table1 as B
ON B.ID = A.parentID
WHERE B.parentID IS NULL