Это типичный иерархический запрос. Рассмотрим решение, которое использует рекурсивный cte:
with cte as (
select masterID rootID, masterID, name, parentid
from mytable
where parentID is null
union all
select c.rootID, t.masterID, t.name, t.parentID
from mytable t
inner join cte c on c.masterID = t.parentID
)
select * from cte
Как прокомментировал The Impaler, вы можете при необходимости изменить начальное условие where parentID is null
на id другого узла.
Сваши примерные данные, это дает:
rootID | masterID | name | parentid
-----: | -------: | :--- | -------:
1 | 1 | A | <em>null</em>
1 | 2 | B | 1
1 | 3 | C | 2
1 | 4 | D | 3
Обратите внимание, что я отслеживал идентификатор корневого объекта, поэтому легче понять, что происходит, если в ваших данных есть несколько корней.
Вы также можете использовать корень для генерации простого списка детей:
with cte as (
select masterID rootID, masterID, name, parentid
from mytable
where parentID is null
union all
select c.rootID, t.masterID, t.name, t.parentID
from mytable t
inner join cte c on c.masterID = t.parentID
)
select rootID, string_agg(masterID, ',') childrenID from cte group by rootID
rootID | childrenID
-----: | :---------
1 | 1,2,3,4
Демонстрация на DB Fiddle