powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
9 сообщений из 9, страница 1 из 1
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101242
Ashot Takiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день.

Я бы хотел спросить о возможности преобразования таблицы PostgreSQL, в которой используется структура ltree в таблицу со структурой вложенных множеств, где для каждого узла определены левая и правая границы. Можно ли это как-то сделать используя представления?
Например, у меня есть таблица в которой существует структура как на картинке:



Объявление таблицы

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
    StructureLtree(id INT PRIMARY KEY, value TEXT, path ltree);
    
    -- Данные:

    pk |  value  |  path  |
    0  |   'A'   |   ''   |
    0  |   'B'   |   '1'  |
    0  |   'C'   |   '2'  |
    0  |   'D'   |  '1.3' |
    0  |   'E'   |  '1.4' |
    0  |   'F'   |  '1.5' |
    0  |   'G'   |  '2.6' |
    0  |   'H'   |  '2.7' |


И мне необходимо преобразовать это табличное представление в следующее:

Код: sql
1.
StructureSets(id INT PRIMARY KEY, value TEXT, lft INT, rgt INT);


, где левая и правая граница узла соответствует правилам определения вложенного множества.

У меня сейчас крутится идея вокруг такого представления. Оно позволяет получить данные о вершине, на каком уровне она находится и её порядковый номер на уровне.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
    CREATE OR REPLACE RECURSIVE VIEW bfs (id, value, path, num_on_level, level) AS
        SELECT id, value, path, row_number() OVER (), 0 as level
        FROM StructureLtree WHERE path ~ '*{0}'
        
        UNION ALL
        
        SELECT C.id, C.value, C.path, row_number() OVER (PARTITION BY P.path), level + 1 
        FROM StructureLtree C JOIN bfs P ON P.path @> C.path 
        WHERE nlevel(C.path) = level + 1;

    -- Данные представления
    id | value | path | num_on_level | level |
    0  |  "A"  |  ""  |      1       |   0   |
    1  |  "B"  | "1"  |      1       |   1   |
    2  |  "C"  | "2"  |      2       |   1   |
    3  |  "D"  |"1.3" |      1       |   2   |
    4  |  "E"  |"1.4" |      2       |   2   |
    5  |  "F"  |"1.5" |      3       |   2   |
    6  |  "G"  |"2.6" |      1       |   2   |
    7  |  "H"  |"2.7" |      2       |   2   |


Но что делать, дальше, понять не могу (как установить границы "A" left = 1, right = 16, "B" left = 2, right = 9 и т.д.)

Может кто-нибудь подсказать идею? Решение должно быть с использованием только представлений или обобщенных табличных выражений.

Заранее благодарю.
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101325
p2.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Хранить дерево ltreёй при наличии рекурсивного sql выглядит странно ввиду избыточности.
Посему предлагаю решение применительно к реляционным обычаям: таблица tr с полями a - ид, b - ссылка на a.

Циклический обход с поворотом только направо разворачивает рекурсию в длинную цепочку с проходом нелистовых узлов дважды. Сиблинги организованы в последовательную цепочку lagом. С учетом еще и not-exists-заныривания в таблицу для определения листовых узлов, такой подход может работать крайне медленно на больших объемах.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
with recursive x as (
   select t.a, t.b, la, ra, 0 lvl,
    'tp' d, 1::bigint le, null::bigint ri
   from (select a, b, lag(a) over(partition by b order by a) la, lead(a) over(partition by b order by a) ra from tr where b is null) t
   union all
   select t.a, t.b, t.la, t.ra, x.lvl+1, 
      case when x.a = t.b  then 'dn'
           when x.a = t.la then 'sb'
           when x.b = t.a  then 'up'
      end,
      case when x.a = t.b  then x.le+1
           when x.a = t.la then x.ri+1 end,
      case when x.a = t.b  then x.le+2
           when x.a = t.la then x.ri+2
           when x.b = t.a  then x.ri+1 end
   from (select a, b, lag(a) over(partition by b order by a) la, lead(a) over(partition by b order by a) ra from tr) t
   join x on x.a = t.b  and t.la is null and x.d != 'up' 
          or x.a = t.la and (x.d = 'up' or not exists (select 1 from tr where b = x.a))
          or x.b = t.a  and x.ra is null and (x.d = 'up' or not exists (select 1 from tr where b = x.a))
)
select a, min(le), max(ri)
from x
group by a
order by a
;

 a | min | max 
---+-----+-----
 A |   1 |  16
 B |   2 |   9
 C |  10 |  15
 D |   3 |   4
 E |   5 |   6
 F |   7 |   8
 G |  11 |  12
 H |  13 |  14
(8 rows)
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101523
Ashot Takiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
p2.,

Спасибо большое за ответ. К сожалению, я не могу изменить структуру исходной таблицы. Есть исходная таблица, в которой отношение узлов между собой задано с помощью ltree. И стоит задача перенести это в таблицу, где отношения заданы с помощью вложенных множеств.

К сожалению, у меня пока не получается придумать реализацию, как такой переход от ltree к nested sets можно сделать с помощью рекурсивного представления/CTE.
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101763
ARTURV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ashot Takiev,

Попробуйте посмотреть
http://habrahabr.ru/post/153861/
может поможет
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101788
p2.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ashot Takievне могу изменить структуру исходной таблицыспособ хранения связей на методику обхода не влияет, только на оператор, проверяющий связь.
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101852
Ashot Takiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
p2.,
Я не очень понимаю, что происходит внутри рекурсии применительно к ltree:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
-- la, ra - левый и правый предок?
-- le, ri - левый и правый индексы

select t.id, t.path, la, ra, 0 lvl,
    'tp' d, 1::bigint le, null::bigint ri
   -- как применить разбиение на партиции в ltree? id разные, path - тоже
   from select id, path, lag(id) over(partition by path order by id) la, lead(id) over(partition by path order by id) ra from StructureLtree where path ~ '*{0}') t
   union all
   select t.a, t.b, t.la, t.ra, x.lvl+1, 
      case 
           -- x.a = t.b - тоже самое, что x.path <@ t.path, то есть вершина из x является потомком вершины из t
           when x.a = t.b  then 'dn'
           -- здесь, насколько я понимаю, если вершина из x является левым предком вершины из t.
           -- не очень понимаю, как такое можно сделать в ltree, разве что упорядочить по path
           when x.a = t.la then 'sb'
           -- x.b = t.a - тоже самое, что x.path @> t.path, то есть вершина из x является предком вершины из t
           when x.b = t.a  then 'up'
      end,
      case 
           -- если вершина из x является потомком вершины из t, то увеличиваем левый индекс
           when x.a = t.b  then x.le+1
           -- если вершина из x является левым предком вершины из t, то увеличиваем правый индекс
           when x.a = t.la then x.ri+1 end,
      case
           -- здесь рассуждения аналогичны первому case
           when x.a = t.b  then x.le+2
           when x.a = t.la then x.ri+2
           when x.b = t.a  then x.ri+1 end
   from (
           -- такой же подзапрос как в нерекурсивной части
           select a, b, lag(a) over(partition by b order by a) la, lead(a) over(partition by b order by a) ra from tr
   ) t
   join x 
          -- если вершина из x является потомком вершины из t и левый индекс вершины из t равен нулю и вершина x не является предком t?
          on x.a = t.b  and t.la is null and x.d != 'up' 
          -- если вершина из x является левым предком вершины из t и x является предком кого-либо?
          or x.a = t.la and (x.d = 'up' or not exists (select 1 from tr where b = x.a))
          -- если x является предком вершины t и правый индекс x нулевой и далее происходит нечто непонятное
          or x.b = t.a  and x.ra is null and (x.d = 'up' or not exists (select 1 from tr where b = x.a))



Буду признателен, если поможете разобраться.
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39101982
p2.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ashot Takiev -- здесь, насколько я понимаю, если вершина из x является левым предком вершины из t.
-- не очень понимаю, как такое можно сделать в ltree, разве что упорядочить по path
к проблеме отцы и дети инцест брата с сестрой никакого отношения не имеет. это row_number нумерация в твоих попытках или связанный список в моих:
select a, b, lag(a) over(partition by b order by a) la, lead(a) over(partition by b order by a) ra from tr
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39102139
Ashot Takiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
p2.,
я не понимаю, как для ltree так дерево развернуть.
Нерекурсивная база понятна — берем элемент, у которого путь пустой.
А дальше как? По идее, можно было бы сначала проиндексировать вершины, отсортировав по значению path (тем самым мы бы получили номера узлов при DFS обходе). А как быть с правыми индексами?
...
Рейтинг: 0 / 0
Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
    #39102713
Ashot Takiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В итоге сделал так (без мудреных рекурсий и прочего):

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
CREATE VIEW anc_and_des AS 
SELECT 
        m.*, 
        -- считаем все дочерние узлы вершины m
        ( SELECT COUNT(*) 
          FROM KeywordLtree AS d
          WHERE m.path @> d.path
        ) AS descendants,
        -- считаем все родительские узлы вершины m
        ( SELECT COUNT(*) 
          FROM KeywordLtree AS a
          WHERE a.path @> m.path
        ) AS ancestors,
        ROW_NUMBER() OVER (ORDER BY m.path) AS rn
    FROM KeywordLtree AS m;

-- левый индекс вложенного множества равен двойному номеру узла при DFS-обходе - число предков (включая сам узел)
-- правый индекс равен двойному номеру узла при DFS-обходе - число предков (включая сам узел) + удвоенное число потомков - 1 (иначе дважды посчитаем текущий узел)
SELECT *, 2 * rn - ancestors AS lft, 2 * rn - ancestors + 2 * descendants - 1 AS rgt
FROM anc_and_des;
...
Рейтинг: 0 / 0
9 сообщений из 9, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как преобразовать ltree-структуру во вложенное множество в PostgreSQL?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]