Как выделить всю иерархию таблицы с уровнями в PostgreSQL

У меня сейчас проблема. У меня есть таблица мест с такой структурой:

  • я бы
  • parent_id
  • имя

Я хочу сделать выбор, чтобы иметь всю иерархию этой таблицы. Вот небольшой пример данных:

(1, null, '123 Barclay St')
(2, 1, 'Floor 1')
(3, 1, 'Floor 2')
(4, 1, 'Floor 3')
(5, 2, 'Hall 1')
(6, 2, 'Room 1')
(7, 2, 'Room 2')
(8, 3, 'Room 3')
(9, null, '10 Thames St')

Очевидно, что порядок в таблице не такой.

Итак, я хочу получить этот результат с помощью моего SELECT (с 9 строками):

123 Barclay St
   Floor 1
      Hall 1
      Room 1
      Room 2
   Floor 2
      Room 3
   Floor 3
10 Thames St

И не этот результат (который я уже знаю, как получить):

10 Thames St
123 Barclay St
   Floor 1
   Floor 2
   Floor 3
      Hall 1
      Room 1
      Room 2
      Room 3

Если вы можете мне помочь, заранее благодарю вас.

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
0
1 973
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот решение с использованием рекурсивных CTE:

WITH RECURSIVE cte AS (
    SELECT LPAD(id::text, 3, '0') AS marker, '   ' AS buffer,
        id, parent_id, name::text
    FROM yourTable t WHERE parent_id IS NULL
    FROM yourTable t WHERE parent_id IS NULL
    UNION ALL
        SELECT t2.marker || ':' || LPAD(t1.parent_id::text, 3, '0') || ':' ||
            LPAD(t1.id::text, 3, '0') AS marker,
            t2.buffer || '   ', t1.id, t1.parent_id, t2.buffer || t1.name
    FROM yourTable t1
    INNER JOIN cte t2
        ON t1.parent_id = t2.id
)

SELECT name FROM cte ORDER BY marker;

Демо

Основная идея здесь состоит в том, чтобы построить строки пути, которые отслеживают полный путь от каждого узла, идущего к его корню (корень задается узлом, чей parent_id - это NULL). Затем мы просто выполняем один ORDER BY на этом пути, чтобы сгенерировать нужный вам порядок.

Я пробовал это решение и подтверждаю его. Большое спасибо.

S. Kaio 09.09.2018 11:53

@ S.Kaio Использование массивов немного проще: dbfiddle.uk/… И, кроме того, вы получаете простой в использовании путь для каждого элемента.

Abelisto 09.09.2018 13:31

@Abelisto Я протестировал ваше решение и мне очень понравилось. Спасибо за помощь :)

S. Kaio 09.09.2018 17:53

Вы не предоставили запросы, которые у вас уже были. Но, насколько я понимаю, вам нужна рекурсивная древовидная структура.

https://www.db-fiddle.com/f/og5HZDHBhBRmP1cDnqgCBB/1

CREATE TABLE rooms (
  id INTEGER, parent_id INTEGER, name TEXT
);

INSERT INTO rooms VALUES
(1, null, '123 Barclay St'),
(2, 1, 'Floor 1'),
(3, 1, 'Floor 2'),
(4, 1, 'Floor 3'),
(5, 2, 'Hall 1'),
(6, 2, 'Room 1'),
(7, 2, 'Room 2'),
(8, 3, 'Room 3'),
(9, null, '10 Thames St');

И запрос:

WITH RECURSIVE tree AS (
    SELECT
        rooms.id,
        rooms.parent_id,
        rooms.name
    FROM
        rooms
    WHERE
        parent_id IS NULL
    UNION ALL
    SELECT
        rooms.id,
        rooms.parent_id,
        rooms.name
    FROM
        tree
        JOIN rooms ON rooms.parent_id = tree.id
)
SELECT
    *
FROM
    tree;

https://www.postgresql.org/docs/current/static/queries-with.html

Извините, если я не предоставлю уже возникшие вопросы, но я сказал, что результат мне не нужен. Я сделал ваш запрос перед этой публикацией, и этот запрос дает мне плохой результат, о котором я сказал. Он возвращает следующую иерархию: Barclay St, Floor 1, Floor 2, Floor 3, Hall 1, ... но не со всей иерархией по уровням. Большое вам спасибо за то, что вы пытались мне помочь. :)

S. Kaio 09.09.2018 12:00

Другие вопросы по теме