Write Element's Ancestry To Postgres Table From Adjacency List
Solution 1:
SQL is a strictly typed language that does not allow the number of columns returned from a SELECT to vary depending on the data it is acting upon. See e.g. Split comma separated column data into additional columns for a discussion.
However, PostgreSQL offers you an array type that you can use to collect values of dynamic size into a single column. The following recursive CTE collects all ancestors of every row into such an array:
withrecursive rec(id, level, parent_id, ancestors) as (
select id, 0, parent_id, array[] :: int[]
from test
where parent_id = id
unionallselect t.id, rec.level +1, t.parent_id, rec.ancestors ||array[t.parent_id]
from test t
join rec on t.parent_id = rec.id
where t.parent_id <> t.id
)
select
rec.id,
rec.level,
rec.ancestors
from rec;
If there's a known limit to the levels, you can select the elements from the array per column:
select
rec.id,
rec.level,
rec.ancestors[1] level1,
rec.ancestors[2] level2,
...
Solution 2:
if the position of the object is not changed in time (i.e. if it was put from the beginning on level 6 it will stay on that level forever) you can introduce some sane id with 7 numbers, showing 7 levels, separated by lets say semicolon(:):
'1:1:1:1:1:1:1'
and then introduce some functional indexes, like:
CREATE INDEX level1_idx ON main_table USING (regexp_split_to_array(id, '\\:')[1])
CREATE INDEX level2_idx ON main_table USING (regexp_split_to_array(id, '\\:')[2])
CREATE INDEX level3_idx ON main_table USING (regexp_split_to_array(id, '\\:')[3])
then you can alaways make an efficient query:
SELECT id, regexp_split_to_array(id, '\\:')[1] as level1, regexp_split_to_array(id, '\\:')[2] as level2, ...
ORDER BY level1, level2, level3 ...
Post a Comment for "Write Element's Ancestry To Postgres Table From Adjacency List"