Рекурсия CTE с несколькими условиями SQL

В базе данных у меня есть 2 следующих фрагмента информации для каждого идентификатора. Компания, которая их контролирует, и компании, в которых они имеют небольшой контроль.

Что-то вроде строк, 2 таблицы (игнорируя некоторые уникальные идентификаторы):

организации

orgid | org_immediate_parent_orgid
1     | 2
2     | 2
3     | 1
5     | 4

Отношение orgid --> org_immediate_parent_orgid означает, что у компании есть родитель. Для меня это актуально только org_immediate_parent_orgid --> orgid материнской компании в качестве дочерней

организация

orgid | affiliated_orgid
2     | 3
2     | 5
4     | 1
1     | 5

orgid -->affiliated_orgid - компания имеет аффилированное лицо

Визуальное представление должно быть примерно таким:

Представление базы данных

На красном — отношения от организаций, на синем — отношения org_affiliations.

Если вы хотите, чтобы все компании, принадлежащие 2 (или дочернему сыну 2), принимали в нем какое-то участие:

select m.org_immediate_parent_orgid
,m.orgid
from oa.organizations m
where m.org_immediate_parent_orgid is not null
start with m.orgid in (Identifiers)
connect by nocycle prior  m.orgid=m.org_immediate_parent_orgid

возвращается

org_immediate_parent_orgid| orgid
1                         | 2
2                         | 2
3                         | 1

Если вы хотите, чтобы все компании были 2 (или аффилированные с сыном 2), они имеют в этом какое-то участие:

select aff.orgid,aff.affiliated_orgid
from oa.org_affiliations aff
where aff.affiliated_orgid is not null
start with aff.orgid in(Identifiers)
connect by nocycle prior  aff.affiliated_orgid =aff.orgid

возвращается

orgid | affiliated_orgid
2     | 3
2     | 5

Итак, из всех возможных отношений:

  • Афф --> Афф
  • Афф --> Саб
  • Саб --> Афф
  • Подпрограмма --> Подпрограмма

Я нахожу только Sub --> Sub (дочерние дочерние), отношения (2 --> 1 и отношения 1 --> 3) и Aff --> Aff, отношения (2 --> 3 и отношения 2 --> 5 ). Также мне требуется 2 отдельных запроса.

Как я могу получить все возможные отношения в одном рекурсивном запросе?

Если я передам идентификатор 2, должно быть возможно следующее возвращение:

Relation | Loop| orgid | children
Sub      | 1   | 2     |2
Sub      | 1   | 2     |1
Aff      | 1   | 2     |3
Aff      | 1   | 2     |5
Sub      | 2   | 1     |3
Aff      | 2   | 1     |5

В каждом цикле проверялись бы сабы и аффилиаты по каждому идентификатору. Повторите для новых детей.

Любая идея о том, как подойти к нему?

TL:DR: 2 таблицы (дочерние\аффилированные), 2 запроса. хочу один запрос, где от компании я могу найти все дочерние компании и филиалы и все возможные комбинации subs\affs. Окончательный ожидаемый результат показан, просто следуйте изображению.

Редактировать: Как прокомментировал Крейг, я исправил вывод.

Edit2: После хорошей помощи Крейга и Боба Джарвиса я продолжаю сталкиваться с проблемами.

Для сбора дочерних компаний следующий код работает безупречно, и результат такой, как мне хотелось бы:

with
relations as
(
select orgid as children,org_immediate_parent_orgid as orgid,'Sub' as relation
from oa.organizations 
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

То же самое для АФФ:

with
relations as
(
select affiliated_orgid as children, orgid as orgid,'Aff' as relation
from oa.org_affiliations    
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

но не может быть "союз всех"?

with
relations as
(
select orgid as children,org_immediate_parent_orgid as orgid,'Sub' as relation
from oa.organizations

UNION ALL

select affiliated_orgid as children, orgid as orgid,'Aff' as relation
from oa.org_affiliations    
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

В sql-разработчике я пошел и проверил «объяснить план и стоимость каждого перехода с 7 до 400 тыс., Просто добавив «союз всех». Есть ли обходной путь? Проблема внутри CTE, в союзе все?

Решение Боба Джарвиса не будет работать в случаях, когда у меня есть comp-sub-sub-aff, или оно находит все дочерние компании компании или все филиалы


person blueomega    schedule 05.08.2011    source источник
comment
+1 за красоту стрижки. впрочем, слишком долго... не слушал :)   -  person Bohemian♦    schedule 05.08.2011
comment
Это слишком долго для меня, чтобы прочитать, понять и ответить!   -  person Kangkan    schedule 05.08.2011
comment
TL:DR: 2 таблицы(дочерние\аффилированные), 2 запроса. хочу один запрос, где от компании я могу найти все дочерние компании и филиалы и все возможные комбинации subs\affs. Окончательный ожидаемый результат показан, просто следуйте изображению.   -  person blueomega    schedule 05.08.2011
comment
@blueomega - Вы когда-нибудь выясняли это или у вас все еще есть проблемы? Если он все еще не работает, я посмотрю еще раз.   -  person Craig    schedule 18.08.2011


Ответы (3)


Переместите это из комментария в фактический ответ и предоставьте то, что, по моему мнению, вам нужно.

Пара вещей.. одна второстепенная.. Я полагаю, что у вас есть метки вашего первого подключения, возвращая вывод назад. Кроме того, я не понимаю, как вы получаете последние две строки в своем окончательном выводе. 4 является родителем 5, а не ребенком, так почему же он появляется? А если его нет, то не будет и последней строчки.

Если я правильно читаю, вы можете использовать что-то вроде:

with
relations as
(
    select
        orgid,
        org_immediate_parent_orgid parent_id,
        'Sub' relation
    from
        organizations
    union all
    select
        orgid,
        null parent_id,
        'Aff' relation
    from
        org_affiliations
    where
        orgid not in (
            select affiliated_orgid
            from org_affiliations
        )
    union all
    select
        affiliated_orgid orgid,
        orgid parent_id,
        'Aff' relation
    from
        org_affiliations
)
select distinct relation, level, parent_id, orgid
from relations
where parent_id is not null
start with orgid = 2
connect by
    nocycle prior orgid = parent_id
order by 2,3,4

Что дает следующий результат:

RELATION|LEVEL|PARENT_ID|ORGID
Sub     |1    |2        |2
Sub     |2    |2        |1
Aff     |2    |2        |3
Aff     |2    |2        |5
Sub     |3    |1        |3
Aff     |3    |1        |5

Самое главное, что 2 таблицы были настроены друг напротив друга (организации имели ссылку на родительскую, аффилиации — на дочернюю). Поэтому я делаю их в том же формате в предложении WITH, а затем использую соединение с объединенным набором.

Кроме того, по какой-то причине Oracle дает первому циклу уровень, отличный от других, поскольку он является ссылкой на себя. Я предполагаю, что если это проблема, вы можете добавить некоторую пользовательскую логику для этого случая.

person Craig    schedule 05.08.2011
comment
Привет, Крейг, обмен метками не проблематичен, системе, которая ест эти данные, все равно. Упс 4 должен быть дочерним по отношению к 5, а не наоборот, отличный улов. В целом, отличная работа, мне уже пора спать, но я проверю ее позже и предоставлю больше отзывов. Теперь любые изменения должны быть легкими :) - person blueomega; 06.08.2011
comment
Кажется, я все еще сталкиваюсь с проблемами, описанными выше. часть запроса с нулевым parent_id, если я правильно понимаю, мне не нужны выходные идентификаторы компаний, у которых больше нет aff - person blueomega; 08.08.2011

Вот начало:

select 'SUB -> SUB' AS TYPE,
       m.orgid AS ORGID,
       m.org_immediate_parent_orgid AS PARENT_OR_AFF
  from organizations m
  where m.org_immediate_parent_orgid is not NULL
  start with m.orgid in (2)
  connect by nocycle prior m.orgid = m.org_immediate_parent_orgid
UNION ALL
select 'AFF -> AFF' AS TYPE,
       aff.orgid AS ORGID,
       aff.affiliated_orgid AS PARENT_OR_AFF
  from org_affiliations aff
  where aff.affiliated_orgid is not NULL
  start with aff.orgid IN (2)
  connect by nocycle prior aff.affiliated_orgid = aff.orgid;

Если вы добавите подзапросы, чтобы получить оставшиеся отношения, все будет хорошо.

Делитесь и наслаждайтесь.

person Bob Jarvis - Reinstate Monica    schedule 05.08.2011

Непроверенный. Сначала создайте представление, чтобы упростить извлечение данных из двух таблиц.

create view related(orgid, relatedid) as 
    select orgid, org_immediate_parent_orgid as relatedid from organizations
    union distinct
    select orgid, affiliated_orgid as relatedid from affiliated;

Теперь мы можем использовать это, чтобы легко итеративно находить все интересные оргиды.

with recursive related_recursive(orgid, relatedid) as (
    select orgid, relatedid from related where relatedid = 2
    union
    select r.origid, rr.relatedid from related_recursive rr, related r
        where rr.orig = r.relatedid
) 
select orgid from related_recursive;

В этом случае вы даже можете удалить столбец relatedid из related_recursive, но это полезно и необходимо, если вы хотите удалить или изменить часть where и выбрать * из related_recursive.

Просто помните, что CTE полностью оцениваются перед основным запросом, поэтому это может привести к увеличению количества страниц до возможной фильтрации в основном запросе.

person Jürgen Strobel    schedule 15.08.2011
comment
Я не видел, чтобы вы использовали соединение, которое, я думаю, специфично для оракула? Не должно быть проблем преобразовать это из стандартного в рекурсивное, если оракул еще не поддерживает его. - person Jürgen Strobel; 15.08.2011