CTE query to root element postgres - postgresql

This is a very general question. I found some questions and discussions on more specific problems on SO, but I am quite sure, that many of you have already solved this one:
input:
A table that has a tree structure in one field.
An arbitrary id of a database record x.
question:
How can I get the root of the tree of x?
I found out that there should be a way to implement this recursively, but I couldn't achieve it yet.

The root element can be found in the same way as child elements of a given root,
but the query must search in the opposite direction.
Take a look at simple demo: --> http://www.sqlfiddle.com/#!17/fdc8a/1
This query retrieves all childrens of a given root:
WITH RECURSIVE childs( id, parent_id )
AS (
-- get parent
SELECT id, parent_id
FROM tab
WHERE id = 10
UNION ALL
-- get all children
SELECT t.id, t.parent_id
FROM childs c
JOIN tab t
ON t.parent_id = c.id
)
SELECT * from childs;
and this query retrieves all parents of a given child node:
WITH RECURSIVE parents( id, parent_id )
AS (
-- get leaf children
SELECT id, parent_id
FROM tab
WHERE id = 14
UNION ALL
-- get all parents
SELECT t.id, t.parent_id
FROM parents p
JOIN tab t
ON p.parent_id = t.id
)
SELECT * from parents
-- WHERE parent_id is null;
if only the root node is needed, a clause WHERE parent_id IS NULL filters out all except the root.

Related

Updating a parent and its children without link between them

I'm facing an issue and hope you will be able to guide me in the right direction.
A facility can have 1..n sub-facilities. Sub-facilities can have 1..n sub-facilities as well. And this can be a never ending relationship.
In the database, we only have keys to connect the parent and first children.
Here's a short schema:
--Facility has : facilityid|parentid
---child1 has : facilityid|parentid to facility
----child2 has : facilityid|parentid to child 1 / no key to main parent
-----child2 has : facilityid|parentid to child 2 / no key to main parent
What I am trying to do is update a column named value and set it to true for a parent and all of its children and sub-children included. Right now, since I am only able to see the first parent and its children but not the sub-children, I can only update the value of those. But that leaves all my sub-children not updated.
Knowing that there is no link between sub-children and main parent, how would I accomplish that?
Here's a query that gives me a parent and all of its children.
WITH attributes AS (
select fa.facilityid, a.id
from caip_attribute a
join caip_facility_attribute fa on a.id = fa.attributeid)
select a.facilityid as parentfacilityid, a.id as parentattributeid, f.id as facilitychildid, fa.attributeid as childattributeid
from attributes a
join caip_facility f on f.parentid = a.facilityid
join caip_facility_attribute fa on fa.facilityid = f.id
join caip_attribute at on at.id = fa.attributeid
where at.definitionTypeKey = 'AUTO_CREATE_PROCESS'
order by f.id asc
The update statement is missing here but this is how I get the values that will later need to be updated.
Thank you!
To update all descendants of a parent you can use a recursive CTE.
Suppose we have a data table with relations set using self-referencing foreign key column parent_id:
CREATE TABLE t (
id SERIAL PRIMARY KEY,
value BOOL DEFAULT FALSE,
parent_id INT,
FOREIGN KEY (parent_id) REFERENCES t(id)
);
First, a query that returns a parent with id = 1 and all it's descendants would be like this:
WITH RECURSIVE children(id, value, parent_id) AS (
SELECT id, value, parent_id FROM t WHERE id = 1
UNION
SELECT t.id, t.value, t.parent_id FROM t
INNER JOIN children ON children.id = t.parent_id
)
SELECT * FROM children
Second, to update a value of parent and all it's descendants we can set value for all rows with ids in our select query:
UPDATE t SET value = true
WHERE id IN (
WITH RECURSIVE children(id, value, parent_id) AS (
SELECT id, value, parent_id FROM t WHERE id = 1
UNION
SELECT t.id, t.value, t.parent_id FROM t
INNER JOIN children ON children.id = t.parent_id
)
SELECT id FROM children
)
Check a demo

SQL SELECT Parent-Child with SORT (...again)

UPDATED:
I have a simple, one level parent child relation table, with following columns:
ID_Asset| Parent_ID_Asset | ProductTitle
I need output grouped by Parent followed by children, and also sorted by Parent and Children Name. My attempts in the fiddle.
See here for details: https://rextester.com/PPCHG20007
Desired order:
9 8 NULL ADONIS Server
7 16 8 ADONIS Designer
8 20 8 ADONIS Portal Module “Control & Release” Package XS
Parent first, than children, while ProductTitle ordered alphabetically.
Thanx all for hints so far.
I would do conditional ordering instead :
select t.*
from table t
order by (case when parent_id is null then id else parent_id end), ProductTitle;
I am assuming you need to sort the data based on parent-child relation.
As far as I understand, you need to sort on the name of the root products, and in between them to show the sub-products, ordered by name, and in between them to show their sub-products, etc.
I guess you are using a recursive cte. You can define a "hierarchy sorting" helper which is a padded number in the current level, and for each level deep, add a suffix with the padded number in the current level, etc.
Something like that:
declare #Products table(ID int, Parent_ID int, ProductTitle varchar(100))
insert into #Products values
(1, NULL, 'ADONIS'),
(2, NULL, 'BACARAT'),
(3, 1, 'Portal Module'),
(4, 1, 'Alhambra'),
(5, NULL, 'ZULU'),
(6, 2, 'Omega')
; with cte as (
select ID, Parent_ID, ProductTitle, FORMAT(ROW_NUMBER() over(order by ProductTitle), '0000') as SortingHelper
from #Products
where Parent_ID is null
union all
select p.ID, p.Parent_ID, p.ProductTitle, cte.SortingHelper + '.' + FORMAT(ROW_NUMBER() over(order by p.ProductTitle), '0000') as SortingHelper
from #Products p
inner join cte on cte.ID = p.Parent_ID
)
select ID, Parent_ID, ProductTitle
from cte
order by SortingHelper
I think this is the ordering you are looking for. This joins each row to its parent (if it exists). Then if there is a TP (parent) record then that is the parent title, ID etc, otherwise the current record must be the parent. I've shown the parent name in the query results for clarity. Then it sorts by
Parent name (so parents and children are together, but in parent name order)
Parent ID (in case two or more parents have the same name/title, this will keep children with the correct parent)
A flag which is 0 for parents, 1 for children, so the parent comes first
The current record name, which will sort children by name/title order
Code is
Select T.*,
isnull(TP.ProductTitle, T.ProductTitle) as ParentName -- This is the parent name, shown for reference
from test T
left outer join Test TP on TP.ID_Asset = T.Parent_ID_Asset --This is the parent record, if it exists
ORDER BY isnull(TP.ProductTitle, T.ProductTitle), --ParentName sort
isnull(TP.ID_Asset, T.ID_Asset), --if two parents have the same title, this makes sure they group with their correct children
Case when T.Parent_ID_Asset is null then 0 else 1 end, --this makes sure the parent comes before the child
T.ProductTitle --Child Sort

PostgreSQL: Order by name after a multiple-level hierarchy sort

I'm currently exporting queries from Oracle to PostgreSQL, and I am stuck on this one which is used to sort directories:
WITH RECURSIVE R AS (
SELECT ARRAY[ID] AS H
,ID
,PARENTID
,NAME
,1 AS level
FROM REPERTORIES
WHERE ID= (SELECT Min(ID) FROM REPERTORIES)
UNION ALL
SELECT R.H || A.ID
,A.ID
,A.PARENTID
,A.NAME
,R.level + 1
FROM REPERTORIES A
JOIN R ON A.PARENTID = R.ID
)
SELECT NAME
,ID
,PARENTID
, level
FROM R
ORDER BY H
It's partially working, each subdirectory is placed after his parent directory or a directory sharing the same parent directory (A directory can have subdirectories which also have subdirectories and so on)
But I need to also sort the directories that are at the same level by their NAME (while, of course, still having their subdirectories right next to them)
How can I achieve this?
Thanks in advance (and sorry if my English is hard to understand)
EDIT: Here is the orignial Oracle query:
SELECT NAME, ID, PARENTID, level
FROM REPERTORIES
CONNECT BY PRIOR ID = PARENTID
START WITH ID = (SELECT Min(ID) FROM REPERTORIES)
ORDER SIBLINGS BY NAME
Similar to the way you construct h, construct an array that contains the path names and order by that.
The "order by H" is throwing me for a loop, however..
In general you need to do your sorting in the recursive part of the query to keep them "grouped" properly.
WITH RECURSIVE R AS (
SELECT ARRAY[ID] AS H
,ID
,PARENTID
,NAME
,1 AS level
FROM REPERTORIES
WHERE ID= (SELECT Min(ID) FROM REPERTORIES)
UNION ALL
SELECT R.H || A.ID
,A.ID
,A.PARENTID
,A.NAME
,R.level + 1
FROM REPERTORIES A
JOIN R ON A.PARENTID = R.ID
order by name
)
SELECT NAME
,ID
,PARENTID
, level
FROM R

T-SQL Some questions about a recursive query (CTE)

I take all items (e.g advertisements) from a tree using CTE (here), but I'm wondering about two things - the hardest part:
1) is it somehow possible to get all category names of the found advertisements ? (in the recursive CTE query, look at the hyperlink above)
2) And (optional) how to get the total found advertisement's count of the each category ? I mean, let's say I found 6 items from 3 categories and I'd like to see the result in that way
category1 (6) -\
| category3 (4)
category2 (2)
Any ideas will be so helpful
for the first question you need to go from down to up, it could be resolved with changing querying order ex:
with CTE (id, pid, name)
as
(
select id, parentid as pid,name
from category
where id = #lowLevelCategory
union all
select CTE.pid as id , category.parentid as pid, category.name
from CTE
inner join category
on category.id = CTE.pid
)
select * from ss
for the second one: you can calculate only count of sub items, but not summ and you need some function for this calculations, because grouping or subselecting can't be in recursive part

How to get the top most parent in PostgreSQL

I have a tree structure table with columns:
id,parent,name.
Given a tree A->B->C,
how could i get the most top parent A's ID according to C's ID?
Especially how to write SQL with "with recursive"?
Thanks!
WITH RECURSIVE q AS
(
SELECT m
FROM mytable m
WHERE id = 'C'
UNION ALL
SELECT m
FROM q
JOIN mytable m
ON m.id = q.parent
)
SELECT (m).*
FROM q
WHERE (m).parent IS NULL
To implement recursive queries, you need a Common Table Expression (CTE).
This query computes ancestors of all parent nodes. Since we want just the top level, we select where level=0.
WITH RECURSIVE Ancestors AS
(
SELECT id, parent, 0 AS level FROM YourTable WHERE parent IS NULL
UNION ALL
SELECT child.id, child.parent, level+1 FROM YourTable child INNER JOIN
Ancestors p ON p.id=child.parent
)
SELECT * FROM Ancestors WHERE a.level=0 AND a.id=C
If you want to fetch all your data, then use an inner join on the id, e.g.
SELECT YourTable.* FROM Ancestors a WHERE a.level=0 AND a.id=C
INNER JOIN YourTable ON YourTable.id = a.id
Assuming a table named "organization" with properties id, name, and parent_organization_id, here is what worked for me to get a list that included top level and parent level org ID's for each level.
WITH RECURSIVE orgs AS (
SELECT
o.id as top_org_id
,null::bigint as parent_org_id
,o.id as org_id
,o.name
,0 AS relative_depth
FROM organization o
UNION
SELECT
allorgs.top_org_id
,childorg.parent_organization_id
,childorg.id
,childorg.name
,allorgs.relative_depth + 1
FROM organization childorg
INNER JOIN orgs allorgs ON allorgs.org_id = childorg.parent_organization_id
) SELECT
*
FROM
orgs order by 1,5;