27 декабря 2012 г.

Рекурсивный запрос на postgres

Актуальная версия этой статьи на моём новом сайте devmark.ru

Рассмотрим составление рекурсивных запросов на PostgreSQL для иерархических данных на примере следующей таблицы:
CREATE TABLE hierarchy_example
(
  id serial NOT NULL,
  name character varying(100),
  parent_id integer,
  CONSTRAINT id_pk PRIMARY KEY (id )
)
Заполним её данными:


parent_id содержит номер записи, которая является родительской по отношению к данной. Если parent_id = null, считаем, что это - корневой элемент иерархии.

Теперь составим запрос для прохода по этой иерархии, от элемента с именем subitem1 до root. На каждой итерации будем добавлять новую строку во временную таблицу temp1.

WITH RECURSIVE temp1 ( id, parent_id, name, path ) AS (
SELECT T1.id, T1.parent_id, T1.name, CAST (T1.name AS VARCHAR (50)) as PATH FROM hierarchy_example T1 WHERE T1.id = 4
union
select T2.id, T2.parent_id, T2.name, CAST ( temp1.PATH ||'->'|| T2.name AS VARCHAR(50)) FROM hierarchy_example T2 INNER JOIN temp1 ON (temp1.parent_id = T2.id))
select * from temp1

Рекурсивный запрос начинается со слов WITH RECURSIVE. Далее следует именованный набор полей временной таблицы temp1, в которую мы будем добавлять данные на каждой итерации.

Внутри рекурсивный запрос (то, что записано в скобках после слова AS) можно разделить на две части, которые объединены ключевым словом union. Первая часть - это запрос для поиска элемента, с которого следует начать рекурсивный запрос. Вторая часть - то, что выполняется в каждой итерации. Здесь мы выбираем номер элемента, номер его родительского элемента, а также для наглядности опеределяем временную переменную path, в которой будет содержаться пройденный путь по иерархии.

В самом конце следует обычный запрос, который выполняется к временной таблице temp1, в которую мы помещали строки в каждой итерации.

Результат выполнения запроса:






Как видим, в столбце path последовательно отображается путь от subitem1 до root. Также обратите внимание, что в столбце name нет элемента item2 - он не входит в данную ветвь иерархии.

4 комментария:

  1. Автор, спасибо тебе большое, ты очень помог!

    ОтветитьУдалить
  2. а подскажите как модифицировать этот скрипт. под такое
    1 root
    2 subroot
    3 subsubroot
    при выводе дерева чаще всего нужно знать уровень вложенности, чтобы по разному отображать элемент (ну глупо табуляцию разную делать) ?

    ОтветитьУдалить
    Ответы
    1. Обратите внимание, как происходит накопление значений из каждой строки в столбце path. Аналогично можно ввести столбец level и прибавлять к нему единицу. Будет что-то вроде temp1.level + 1.

      Удалить
  3. Поддержу предыдущего комментатора, никак не мог понять запутанное объяснение в офф доке постгреса, а ты все объяснил прямо по шагам!
    Спасибо большое!!!

    ОтветитьУдалить