I want to write a 1 to n-hierarchy that's stored as an adjacency list to a table that lists each of an element's ancestors. I'm using a Postgres database (Postgres 10, but the machine on which the code is to be deployed runs Postgres 9.x).
Sample input table (adjacency list):
INSERT INTO public.test (id, name, parent_id)
VALUES (1, 't', 1),
(11, 't1', 1),
(12, 't2', 1),
(13, 't3', 1),
(111, 't11', 11),
(112, 't12', 11),
(121, 't21', 12),
(14, 't4', 1),
(141, 't41', 14),
(142, 't42', 14)
As a result I would like a table that looks like this (just a few rows shown; furthermore, the real-life problem I'm trying to solve has seven hierarchical levels instead of just two):
+-----+-------+--------+--------+
| id | level | level0 | level1 |
+-----+-------+--------+--------+
| 1 | 0 | NULL | NULL |
| 11 | 1 | 1 | NULL |
| 12 | 1 | 1 | NULL |
| 111 | 2 | 1 | 11 |
+-----+-------+--------+--------+
id
is the element's id, level
is the level at which this element is located within the hierarchy (0 being the root level), level0/1
is the element's ancestor at the respective level.
I'm new to SQL, so I haven't got any code I could show you. Googling has told me that I probably neet to use a recursive CTE to obtain the desired result and perform a self-join, but I haven't been able to figure out how to do it. Thanks for your help.
EDIT
This is what I have tried so far:
WITH RECURSIVE cte AS
(
SELECT m.id AS id,
0 AS level,
m.parent_id AS level0,
m.parent_id AS level1,
m.parent_id AS parent
FROM public.test AS m
WHERE m.parent_id IS NULL
UNION ALL
SELECT
m.id,
cte.level + 1,
cte.parent AS level0,
cte.parent AS level1,
m.parent_id AS parent
FROM public.test AS m
INNER JOIN cte
ON m.parent_id = cte.id
)
SELECT *
FROM cte;
Of course, setting level0
and level1
to the element's parent doesn't yield the desired result, but I had to set it to something and haven't got further than this.