Рекурсивный иерархический запрос Oracle с условием на уровне листьев

Я хочу сделать иерархический запрос с условием на уровне листа.

Я хотел бы, чтобы запрос отфильтровал все отношения отца и ребенка, где конечный уровень удовлетворяет условию: id LIKE '3%'

Таблица т:

ParentId,Id
INSERT INTO t VALUES ('VTE', 'VTP');
INSERT INTO t VALUES ('VTP', '202');
INSERT INTO t VALUES ('SER', '606');
INSERT INTO t VALUES ('SER', '609');
INSERT INTO t VALUES ('GIF', '301');
INSERT INTO t VALUES ('ECH', '302');
INSERT INTO t VALUES ('PUB', 'MER');
INSERT INTO t VALUES ('MER', '312');
INSERT INTO t VALUES ('MER', '313');
INSERT INTO t VALUES ('MER', '314');
INSERT INTO t VALUES ('MES', '318');
INSERT INTO t VALUES ('PUB', 'PRE');
INSERT INTO t VALUES ('PUB', 'PAP');
INSERT INTO t VALUES ('STA', '317');
INSERT INTO t VALUES ('NIV', 'ANS');
INSERT INTO t VALUES ('ZNM', '497');
INSERT INTO t VALUES ('ZNU', '496');
INSERT INTO t VALUES ('ANS', 'ZNC');
INSERT INTO t VALUES ('ZNC', '491');
INSERT INTO t VALUES ('NUL', 'NIV');
INSERT INTO t VALUES ('NIV', 'VTE');
INSERT INTO t VALUES ('VTE', 'VTC');
INSERT INTO t VALUES ('VTC', '100');
INSERT INTO t VALUES ('VTP', '204');
INSERT INTO t VALUES ('VTP', '205');
INSERT INTO t VALUES ('VTA', '500');
INSERT INTO t VALUES ('SER', '600');
INSERT INTO t VALUES ('NIV', 'PUB');
INSERT INTO t VALUES ('ECH', '303');
INSERT INTO t VALUES ('MER', '305');
INSERT INTO t VALUES ('MER', '306');
INSERT INTO t VALUES ('MER', '309');
INSERT INTO t VALUES ('PAP', '605');
INSERT INTO t VALUES ('SEP', 'PBC');
INSERT INTO t VALUES ('PBC', '601');
INSERT INTO t VALUES ('SEP', 'STA');
INSERT INTO t VALUES ('NIV', 'TRA');
INSERT INTO t VALUES ('ZNP', '498');
INSERT INTO t VALUES ('ANS', 'ZNM');
INSERT INTO t VALUES ('ANS', 'ZNE');
INSERT INTO t VALUES ('ANS', 'ZNR');
INSERT INTO t VALUES ('ZNR', '493');
INSERT INTO t VALUES ('ZNF', '492');
INSERT INTO t VALUES ('VTC', '101');
INSERT INTO t VALUES ('VTC', '102');
INSERT INTO t VALUES ('VTE', 'VTA');
INSERT INTO t VALUES ('VTE', 'SER');
INSERT INTO t VALUES ('AUT', '900');
INSERT INTO t VALUES ('PUB', 'CPR');
INSERT INTO t VALUES ('MER', '310');
INSERT INTO t VALUES ('MER', '311');
INSERT INTO t VALUES ('MER', '604');
INSERT INTO t VALUES ('PUB', 'MES');
INSERT INTO t VALUES ('MES', '316');
INSERT INTO t VALUES ('SEP', 'RSF');
INSERT INTO t VALUES ('RSF', '608');
INSERT INTO t VALUES ('TRA', 'TRP');
INSERT INTO t VALUES ('TRP', '603');
INSERT INTO t VALUES ('ANS', 'ZNP');
INSERT INTO t VALUES ('ANS', 'ZNU');
INSERT INTO t VALUES ('ANS', 'ZNG');
INSERT INTO t VALUES ('ANS', 'ZNF');
INSERT INTO t VALUES ('VTC', '104');
INSERT INTO t VALUES ('VTC', '105');
INSERT INTO t VALUES ('VTP', '200');
INSERT INTO t VALUES ('VTP', '201');
INSERT INTO t VALUES ('VTP', '203');
INSERT INTO t VALUES ('VTA', '400');
INSERT INTO t VALUES ('VTE', 'AUT');
INSERT INTO t VALUES ('CPR', '602');
INSERT INTO t VALUES ('PUB', 'GIF');
INSERT INTO t VALUES ('PUB', 'ECH');
INSERT INTO t VALUES ('MER', '307');
INSERT INTO t VALUES ('MER', '308');
INSERT INTO t VALUES ('PRE', '304');
INSERT INTO t VALUES ('PRE', '315');
INSERT INTO t VALUES ('NIV', 'SEP');
INSERT INTO t VALUES ('TRP', '607');
INSERT INTO t VALUES ('ANS', 'ZNA');
INSERT INTO t VALUES ('ZNA', '499');
INSERT INTO t VALUES ('ZNG', '495');
INSERT INTO t VALUES ('ZNE', '494');
COMMIT;

Сначала я подумал, что могу использовать что-то вроде:

SELECT ParentId, Id
FROM t
WHERE  id LIKE '3%' 
start with ParentId ='NIV'
CONNECT BY PRIOR Id = ParentId

Например, отец 302 – ECH. Отец ECH – PUB. Отец PUB – NIV.

Но запрос отображает только отношения уровня 0: отец 302 - ECH.

Он отбрасывает все более высокие уровни в корень: отец ECH - PUB, отец PUB - NIV.

Я придумал решение ниже.

Но цикл «потомок-родитель» жестко запрограммирован, что противоречит цели иерархического запроса, в котором автоматически определяется количество взаимосвязей «потомок-родитель».

SELECT parentid, id
FROM t
WHERE id IN 
  (SELECT parentid
   FROM t
   WHERE 
   id IN 
     (SELECT parentid
      FROM t
      WHERE id LIKE '3%'
      start with parentid='NIV' 
      CONNECT BY PRIOR id = parentid)
      start with parentid ='NIV' 
      CONNECT BY PRIOR id = parentid)
OR 
ccomuse IN 
  (SELECT parentid 
   FROM t
   WHERE id LIKE '3%'
   start with parentid ='NIV' 
   CONNECT BY PRIOR id = parentid )
OR 
id LIKE '3%' 
start with parentid ='NIV' 
CONNECT BY PRIOR id = parentid 

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


person user2204154    schedule 24.03.2013    source источник


Ответы (3)


Вы смотрели на функцию sys_connect_by_path() для детализации элементов в иерархии и фильтрации самых правых?

http://docs.oracle.com/cd/B28359_01/server.111/b28286/functions171.htm#i1038266

person David Aldridge    schedule 24.03.2013
comment
Спасибо за предложение. Возможно, я что-то упустил из виду, но я действительно не понимаю, как я мог использовать функцию sys_connect_by_path() для возврата таблицы с двумя столбцами с отношениями Child-Father на основе фильтра на уровне листа. - person user2204154; 24.03.2013

Попробуйте построить дерево не с того конца :-)

SELECT distinct ParentId, Id
FROM t
start with Id LIKE '3%' 
CONNECT BY Id = prior ParentId and Id <> 'NIV'

скрипка

person Egor Skriptunoff    schedule 24.03.2013

Если вы хотите отфильтровать все отношения "отец-потомок", где конечный уровень удовлетворяет условию, вам необходимо указать условие в предложении connect by. Это удалит ненужный узел и его дочерние узлы из вывода. Если вы укажете условие в предложении where, дочерние узлы будут отображаться без родительского узла, поэтому вывод не будет иметь смысла.

person S.Roshanth    schedule 06.08.2017