Postgresql query for getting n-level parent-child relation stored in a single table
With Postgres you can use a recursive common table expression:
with recursive rel_tree as (
select rel_id, rel_name, rel_parent, 1 as level, array[rel_id] as path_info
from relations
where rel_parent is null
union all
select c.rel_id, rpad(' ', p.level * 2) || c.rel_name, c.rel_parent, p.level + 1, p.path_info||c.rel_id
from relations c
join rel_tree p on c.rel_parent = p.rel_id
)
select rel_id, rel_name
from rel_tree
order by path_info;
SQLFiddle based on your example: http://sqlfiddle.com/#!11/59319/19
(I replaced the spaces for indention with underscores as SQLFiddle doesn't display the spaces correctly)
PostgreSQL finding nearest parent value of a certain level
You can put a stop condition on the first matching partner in the recursion, then filter the result:
WITH RECURSIVE tree AS (
SELECT "ChildDisplayID" as initialid, "ChildDisplayID", "ParentID", "Rank", 1 as level
FROM table1
WHERE "ChildDisplayID" = {{some ChildID}}
UNION ALL
SELECT t.initialid, t1."ChildDisplayID", t1."ParentID", t1."Rank", t.level + 1
FROM table1 t1
INNER JOIN tree t ON t."ParentID" = t1."ChildDisplayID"
WHERE t."Rank" <> 'Partner'
)
SELECT *
FROM tree
WHERE "Rank" = 'Partner'
It seems like you have a hierarchy where each child has just one parent, so there should be only one match, or no match at all.
Need help to construct the query from parent-child structure table
Here is an recursive CTE for your query. I haven't finished the formatting but it gives you the recursive CTE.
You haven't specified your RBMS, this is in SQL server. See dbFiddle link at the bottom.
create table nodes(
Rec int,
Node varchar(5),
Parent_Node varchar(5));
insert into nodes values
(1,'A',null),
(2,'B',null),
(3,'C1','A'),
(4,'EE','A'),
(5,'EF','B'),
(6,'G1','B'),
(7,'HH','C1'),
(8,'IU','C1'),
(9,'JK','EF'),
(10,'JA','G1'),
(11,'JJ','G1'),
(12,'OT','HH'),
(13,'PT','JA');
with noder as
(
select
Parent_Node n1,
Node n2,
cast(null as varchar(5)) n3,
cast(null as varchar(5)) n4,
cast(null as varchar(5)) n5
from nodes
union all
select
Parent_node,n1,n2,n3,n4
from nodes
join noder on n1 = Node
)
select *
from Noder
where n1 is not null
n1 | n2 | n3 | n4 | n5
:- | :- | :--- | :--- | :---
A | C1 | null | null | null
A | EE | null | null | null
B | EF | null | null | null
B | G1 | null | null | null
C1 | HH | null | null | null
C1 | IU | null | null | null
EF | JK | null | null | null
G1 | JA | null | null | null
G1 | JJ | null | null | null
HH | OT | null | null | null
JA | PT | null | null | null
G1 | JA | PT | null | null
B | G1 | JA | PT | null
C1 | HH | OT | null | null
A | C1 | HH | OT | null
B | G1 | JJ | null | null
B | G1 | JA | null | null
B | EF | JK | null | null
A | C1 | IU | null | null
A | C1 | HH | null | null
db<>fiddle here
Get access from tables with parent child relationship
The problem here is that you start your hierarchy with nodes having b
as a parent. These are nodes c
and d
. Node c
has access 2
explicitly assigned, but node d
does not. The query does not "see" the access of 3
assigned to node b
because that row is not included in the query.
This query is how I would approach the problem. It generates the entire hierarchy and then limits it to rows that have b
as the first node in its path.
Fiddle Here
with recursive descendants as (
select h.parent, h.node as descendant, 1 as level, ad.access,
array[h.node]::text[] as path
from hierarchy h
left join accessdetails ad on ad.node = h.node
union all
select d.parent, s.node, d.level + 1,
coalesce(ad.access, d.access), d.path||s.node::text
from descendants as d
join hierarchy s
on d.descendant = s.parent
left join accessdetails ad on ad.node = s.node
)
select descendant, level, access, path
from descendants
where path[1] = 'b'
order by parent, level, descendant ;
postgres - with recursive
First of all, your (2, 'grandparent', null)
should be (3, 'grandparent', null)
if it really is a grandparent. Secondly, your (implicit) join condition in the recursive half of your query is backwards, you want to get the parent out of rt.levelparent
rather than t.parent_level
:
WITH RECURSIVE recursetree(level_id, levelparent) AS (
SELECT level_id, parent_level
FROM level
WHERE level_id = 197
UNION ALL
SELECT t.level_id, t.parent_level
FROM level t JOIN recursetree rt ON rt.levelparent = t.level_id
-- join condition fixed and ANSI-ified above
)
SELECT * FROM recursetree;
Parent/child table with include/exclude items
demo:db<>fiddle
WITH RECURSIVE items AS (
SELECT -- 1
group_id,
array_agg(name)
FROM
group_Item
GROUP BY group_id
UNION
SELECT DISTINCT
parent_id,
array_agg(unnest) FILTER (WHERE bool_and) OVER (PARTITION BY parent_id) -- 5
FROM (
SELECT
parent_id,
unnest,
bool_and(contains) OVER (PARTITION BY parent_id, unnest) -- 4
FROM items i
JOIN group_children gc -- 2
ON i.group_id = gc.children_id,
unnest(array_agg) -- 3
) s
)
SELECT * FROM items
- The non-recursive part aggregates all names per
group_id
- Recursive part: Joining the chilren against their parents
- Expanding the name arrays into one element per row.
This results in:
| group_id | array_agg | id | parent_id | children_id | contains | unnest |
|----------|-----------|----|-----------|-------------|----------|--------|
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | aaa |
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | ccc |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | aaa |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | bbb |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | aaa |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | ccc |
- Now you have the unnested names. Now you want to find the ones that have to be excluded. Taking the bbb element for the
parent_id = 2
: There is one row withcontains = true
and one withcontains = false
. This should be excluded. Therefore all the names perparent_id
have to be grouped. The contains values can be aggregated with boolean operators. The aggregate functionbool_and
gives onlytrue
if all elements aretrue
. Sobbb
would get afalse
(The aggregation needs to be done as a window function becauseGROUP BY
is not allowed within the recursive part for some reasons):
Result:
| parent_id | unnest | bool_and |
|-----------|--------|----------|
| 2 | aaa | true |
| 2 | bbb | false |
| 2 | bbb | false |
| 2 | ccc | false |
| 3 | aaa | false |
| 3 | aaa | false |
| 3 | bbb | true |
| 3 | ccc | false |
- After that the unnested names can be grouped per
parent_id
. TheFILTER
clause only aggregates the elements where thebool_and
istrue
. Of course you need to do this in a window function again. This creates duplicate records which can be removed by theDISTINCT
clause
Final result (which of course could be filtered by the element 1
):
| group_id | array_agg |
|----------|-----------|
| 5 | {bbb,ccc} |
| 4 | {aaa,bbb} |
| 6 | {aaa,bbb} |
| 7 | {aaa,ccc} |
| 2 | {aaa} |
| 3 | {bbb} |
| 1 | {aaa} |
PostgreSQL recursive parent/child query
To get all children for subject 1, you can use
WITH RECURSIVE c AS (
SELECT 1 AS id
UNION ALL
SELECT sa.child_id
FROM subject_associations AS sa
JOIN c ON c.id = sa. parent_id
)
SELECT id FROM c;
Related Topics
SQL Queries on String Columns - Sorting According to Language
How to Represent a Many-To-Many Relationship in a Relational Database
SQL Delete Records Within a Specific Range
Importing .SQL File on Windows to Postgresql
Remove Trailing Spaces and Update in Columns in SQL Server
How to Specify "Close Existing Connections" in SQL Script
SQL Server 2008 Open Master Key Error Upon Physical Server Change Over
What Are the Reasons *Not* to Use a Guid for a Primary Key
Implementing a Total Order Ranking in Postgresql 8.3
Identify a 3-Column Pk Duplicate in Vba Access
How to Use Explain Plan to Optimize Queries
SQL Update If Parameter Is Not Null or Empty
Correct Way to Select from Two Tables in SQL Server with No Common Field to Join On
How to Query on Table Returned by Stored Procedure Within a Procedure