Полная иерархия Oracle SQL для любого узла - PullRequest
0 голосов
/ 09 сентября 2018

Я хотел бы создать поисковый запрос с полной иерархией в базе данных Oracle SQL. У меня есть следующая таблица, которая называется "item".

id   name  parent_id
1    A     NULL
2    B     1
3    C     2
4    D     3
5    E     2

Вводом является любой заданный идентификатор из столбца идентификатора. Он должен найти всех детей этого идентификатора, их детей. А также родители и их родители. На данный момент у меня следующий запрос:

select distinct m.id, m.parent_id
    from item m
connect by prior m.id = m.parent_id
    start with m.parent_id IN (
        select m.parent_id
        from item m
        connect by m.id = prior m.parent_id
    start with m.id = 3
    union
    select m.parent_id
        from item m
    where m.parent_id = 3);

В данный момент кажется, что он работает только так, что родитель, у которого нет родителя (столбец parent_id равен нулю), не выбран. В противном случае, похоже, работает. Также, если бы мой заданный запрос мог быть упрощен, я был бы также признателен за это.

EDIT

Я думаю, что получил желаемый результат с помощью следующего запроса:

select m.id
    from item m
    start with m.id in (
        select m.id
        from item m
        where connect_by_isleaf = 1
        start with m.id = 3
        connect by m.id = prior m.parent_id
    )
    connect by m.parent_id = prior m.id;

Теперь у меня есть следующая проблема. начать с m.id = 3 . Проблема в том, что я хотел бы создать представление из всего этого запроса. Но так как значение m.id меняется от запроса к запросу, я не могу добавить его в качестве параметра. Также есть возможность закомментировать , начиная с m.id = 3 , а затем он вернет все иерархии между всеми элементами. Есть ли способ создать соединение? Например: я бы запросил все эти отношения всех предметов, а затем по некоторым условиям получил бы только определенные отношения предметов.

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

Установка Oracle :

CREATE TABLE item ( id, name, parent_id ) AS
  SELECT 1, 'A', NULL FROM DUAL UNION ALL
  SELECT 2, 'B',    1 FROM DUAL UNION ALL
  SELECT 3, 'C',    2 FROM DUAL UNION ALL
  SELECT 4, 'D',    3 FROM DUAL UNION ALL
  SELECT 5, 'E',    2 FROM DUAL;

Вы можете получить предмет и всех его предков, используя:

SELECT *
FROM   item
START WITH id = 2
CONNECT BY PRIOR parent_id = id

и вы можете получить всех потомков этого предмета, используя:

SELECT *
FROM   item
START WITH parent_id = 2
CONNECT BY PRIOR id = parent_id

И вы можете объединить их, используя UNION ALL. Тем не менее, это ставит сопоставленный элемент сначала, затем предков, в порядке увеличения предков, затем потомков в порядке уменьшения предков ..., что может сбивать с толку.

Таким образом, вы можете изменить порядок запроса, чтобы поместить оба в согласованный порядок:

SELECT *
FROM   (
  SELECT *
  FROM   item
  START WITH id = 2
  CONNECT BY PRIOR parent_id = id
  ORDER BY LEVEL DESC
)
UNION ALL
SELECT *
FROM   (
  SELECT *
  FROM   item
  START WITH parent_id = 2
  CONNECT BY PRIOR id = parent_id
  ORDER SIBLINGS BY name
);

выход

ID NAME PARENT_ID
-- ---- ---------
1  A    -
2  B    1
3  C    2
4  D    3
5  E    2
0 голосов
/ 10 сентября 2018

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

WITH rek AS (SELECT item.id
                  , item.name
                  , connect_by_root item.id root_id
               FROM item
              START WITH parent_id IS null
            CONNECT BY NOCYCLE parent_id = PRIOR id)
SELECT startItem.id startId
     , startItem.name startName
     , childItem.id childID
     , childItem.name childName
  FROM rek startItem
  JOIN rek childItem
  USING (root_id)
-- WHERE startItem.id = 3 -- This would be done from outside the view

Подзапрос rek связывает всех одноуровневых элементов дерева с корневым элементом.Тогда вам нужно только дважды использовать этот запрос и подключить его через корневой элемент, чтобы получить все элементы, которые связаны через отношение «родитель-потомок».

Если вы хотите уменьшить набор результатов, вы можете использовать SYS_CONNECT_BY_PATH длясделайте так:

WITH rek AS (SELECT item.id
                  , item.name
                  , connect_by_root item.id root_id
                  , SYS_CONNECT_BY_PATH(item.id, '/') path
               FROM item
              START WITH parent_id IS null
            CONNECT BY NOCYCLE parent_id = PRIOR id)
SELECT startItem.id startId
     , startItem.name startName
     , childItem.id childID
     , childItem.name childName
     , childItem.path 
  FROM rek startItem
  JOIN rek childItem
    ON startItem.root_id = childItem.root_id
    AND (startItem.path LIKE childItem.path||'/%'
      OR childItem.path LIKE startItem.path||'/%'
      OR childItem.id = startItem.id)

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

...