powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Найти родителя по связям (иерархия)
4 сообщений из 4, страница 1 из 1
Найти родителя по связям (иерархия)
    #39803376
Adtain
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Господа, проблема следующая. Есть таблица RECS с записями где содержаться guid'ы:
idguid1fldkjs2lkgfjg3fpsdfk4fdsfkf5lmfsd

Есть таблица LINKS , содержащая ссылки на выше описанную таблицу RECS :
id_ref1id_ref2type11203040510521531541

id_ref1 - FK1 на RECS .ID (not null)
id_ref2 - FK2 на RECS .ID (может быть пустым)
type - тип ссылки: 0 ссылка на запись нижнего уровня, 1 ссылка на запись верхнего уровня

По примеру, получается такая иерархия:

3 уровень: RECS .ID = 2,3,4
2 уровень: RECS .ID = 5
1 уровень: RECS .ID = 1

Задача состоит в том, чтобы по любой записи из таблицы RECS (т.е. я могу указать любой ID = 1, 2, 3, 4, 5 и т.д.) найти все связные элементы, используя LINKS , и проставить им GUID от записи 1-го уровня.

Т.е.:
idguidmain_guid1fldkjsfldkjs2lkgfjgfldkjs3fpsdfkfldkjs4fdsfkffldkjs5lmfsdfldkjs

На ум приходит ужасная конструкция вида:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
-- получить все возможные зависимости
with tmp1 as (select l.id_ref1, l.id_ref2, l.type
                from links l
               where l.id_ref2 is not null
              connect by nocycle l.id_ref1 = prior l.id_ref2
                              or l.id_ref1 = prior l.id_ref1
                              or l.id_ref2 = prior l.id_ref1
                              or l.id_ref2 = prior l.id_ref2
               start with l.id_ref1 = 3 -- например
               group by l.id_ref1, l.id_ref2, l.type),

-- перевернуть ссылки в одну сторону
     tmp2 as (select l.id_ref1, l.id_ref2
                from tmp1 t
               where l.type = 0
               union
              select l.id_ref2, l.id_ref1
                from tmp1 t
               where l.type = 1)

-- тут должно быть вычисление guid-а 1-уровня в соответствии с каждым связанным элементом
select t.*
  from tmp2 t


Как можно избавиться от этого монстра или же как отрубить ему пару голов?
По форуму лазил долго, но ключа не распознал и прямого ответа не нашел.
...
Рейтинг: 0 / 0
Найти родителя по связям (иерархия)
    #39803464
rpovarov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Код: plsql
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.
with recs (id, guid) as
(
    select 1, 'fldkjs' from dual union all
    select 2, 'lkgfjg' from dual union all
    select 3, 'fpsdfk' from dual union all
    select 4, 'fdsfkf' from dual union all
    select 5, 'lmfsd'  from dual 
)
, links (id_ref1, id_ref2, type) as 
(
    select 1, null, 1 from dual union all
    select 2, null, 0 from dual union all
    select 3, null, 0 from dual union all
    select 4, null, 0 from dual union all
    select 5, 1,    0 from dual union all
    select 5, 2,    1 from dual union all
    select 5, 3,    1 from dual union all
    select 5, 4,    1 from dual 
)
select 
    id, guid,
    ( select guid from recs where id =
        (
            select id from 
            (
                select l.id_ref1 as id, null      as parent_id from links l where l.type = 1 and id_ref2 is null
                union
                select l.id_ref1 as id, l.id_ref2 as parent_id from links l where l.type = 0 and id_ref2 is not null
                union
                select l.id_ref2 as id, l.id_ref1 as parent_id from links l where l.type = 1 and id_ref2 is not null
            )
            where parent_id is null connect by id = prior parent_id start with id = r.id
         )    
    ) main_guid
from recs r;
...
Рейтинг: 0 / 0
Найти родителя по связям (иерархия)
    #39803511
Adtain
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rpovarov,

Спасибо большое за предоставленный вариант, но решение немножко не подходит,
Во-первых, у меня в RECS большой набор данных и я не хочу полностью его сканить, а хочу задать ID и найти все записи связанные с этим ID. И их уже идентифицировать.
Во-вторых, я конечно сам виноват, не уточнил, у меня может быть более одного "корня в дереве", тогда следует выбрать 1 из них по минимальному ID. Также у меня может не быть записей в LINKS вообще. Либо быть всего 1 строка, связывающая 2 записи из RECS

Вот что сам нагородил, даже как-то работает, но выглядит ужасно:
Код: plsql
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.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
  procedure testFunc(pId in number) as
    lMainGuid varchar2(200);
  begin
    -- получаю весь список ссылок и переворачиваю их в одну сторону
    for rec in (with tmp as (select decode(l.type, 1, l.id_ref2, l.id_ref1) id_ref1,
                                    decode(l.type, 1, l.id_ref1, l.id_ref2) id_ref2
                               from links l
                              where l.id_ref2 is not null
                            connect by nocycle l.id_ref1 = prior l.id_ref2
                                            or l.id_ref1 = prior l.id_ref1
                                            or l.id_ref2 = prior l.id_ref1
                                            or l.id_ref2 = prior l.id_ref2
                              start with l.id_ref1 = pId or l.id_ref2 = pId
                              group by decode(l.type, 1, l.id_ref2, l.id_ref1),
                                       decode(l.type, 1, l.id_ref1, l.id_ref2))

                select l.id_ref1,
                       l.id_ref2,
                       dense_rank() over(partition by l.id_ref1 order by l.lvl desc) isLast -- беру только корневые записи
                  from (-- нахожу родителей для каждого элемента
                        select connect_by_root l.id_ref1 id_ref1,
                               l.id_ref2,
                               level lvl
                          from tmp l
                       connect by nocycle l.id_ref1 = prior l.id_ref2) l
                 order by l.id_ref2 -- если для одной записи несколько корневых, беру первое
               )
    loop
      if rec.isLast = 1 then
        -- делаем первый родительский guid как main
        if lMainGuid is null then
          update recs r
             set r.main_guid = r.guid
           where r.id = rec.id_ref2
          returning r.guid into lMainGuid;
        -- все остальные родительские guid'ы отмечаем первым выбранным
        else
          update recs r
             set r.main_guid = lMainGuid
           where r.id = rec.id_ref2
             and (r.main_guid is null or r.main_guid != lMainGuid);
        end if;

        -- остальные записи отмечаем родительским guid'ом
        update recs r
           set r.main_guid = lMainGuid
         where r.id = rec.id_ref1
           and (r.main_guid is null or r.main_guid != lMainGuid);
      end if;
    end loop;

    -- если записей в таблице links нет
    if lMainGuid is null then
      update recs r
         set r.main_guid = r.guid
       where r.id = pId
         and (r.main_guid is null or r.main_guid != r.guid);  
    end if;
  end;
...
Рейтинг: 0 / 0
Найти родителя по связям (иерархия)
    #39803842
rpovarov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Adtain,

автору меня в RECS большой набор данных и я не хочу полностью его сканить, а хочу задать ID и найти все записи связанные с этим ID. И их уже идентифицировать.
Не проблема, сделайте главным подселект из LINKS (убрав where parent_id is null), а в RECS ищите только найденный путь.
автору меня может быть более одного "корня в дереве",
А что делает их корнями? Почему тогда корнем не является тот узел, из которого они выходят?
авторТакже у меня может не быть записей в LINKS вообще
И как тогда вы собираетесь искать корни и листья?
авторЛибо быть всего 1 строка, связывающая 2 записи из RECS
По какому критерию одну из них назначают корнем?

Показывайте спорные варианты входных данных (в виде with ..., как у меня), описывайте полные правила. Иначе помогать желания не возникает.
...
Рейтинг: 0 / 0
4 сообщений из 4, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Найти родителя по связям (иерархия)
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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