Oracle CONNECT BY рекурсивный и возвращаемое значение соответствует - PullRequest
2 голосов
/ 13 февраля 2020

В следующем примере:

ТАБЛИЦА

ID  NAME    ATTR
-----------------
1   A1      ROOT
2   A2  
3   A3      VALX
4   A4  
5   A5  
6   A6  

СВЯЗЬ

ID  CHILD_ID    PARENT_ID
-------------------------
1      6            4
2      5            4
3      4            3
4      3            1
5      2            1

СХЕМА

SCHEMA

Мне нужен запрос, чтобы получить значение столбца ATTR РОДИТЕЛЯ, когда оно отличается от нуля. Повышайте уровни, пока не получите первый матч. Например с ID 6:

ID   NAME    NAME_PARENT     ATTR_PARENT
-----------------------------------------
6     A6          A3            VALX 

Я пробовал с:

select T.ID, T.NAME, T2.NAME PARENT_NAME, T2.ATTR ATTR_PARENT
from TABLE T
INNER JOIN RELATIONSHIP R
ON R.CHILD_ID = T.ID
INNER JOIN TABLE T2
ON T2.ID = R.PARENT_D
WHERE T2.ATTR IS NOT NULL
START WITH T.ID = 6
CONNECT BY T.ID = PRIOR R.PARENTID 
--and R.PARENTID != prior T.ID

И извините за мой плохой английский sh

Ответы [ 2 ]

2 голосов
/ 13 февраля 2020

Вместо использования [в основном устаревшего] предложения CONNECT BY вы можете использовать стандартные рекурсивные SQL CTE (общие выражения таблиц).

Например:

with
n (id, name, name_parent, attr_parent, parent_id, lvl) as (
  select t.id, t.name, b.name, b.attr, r.parent_id, 1
  from t
  join r on t.id = r.child_id
  join t b on b.id = r.parent_id
  where t.id = 6 -- starting node
 union all 
  select n.id, n.name, b.name, b.attr, r.parent_id, lvl + 1
  from n
  join r on r.child_id = n.parent_id
  join t b on b.id = r.parent_id
  where n.attr_parent is null
)
select id, name, name_parent, attr_parent 
from n
where lvl = (select max(lvl) from n)

Результат:

ID  NAME  NAME_PARENT  ATTR_PARENT
--  ----  -----------  -----------
6   A6    A3           VALX       

Для справки я использовал скрипт данных:

create table t (
  id number(6),
  name varchar2(10),
  attr varchar2(10)
);

insert into t (id, name, attr) values (1, 'A1', 'ROOT');
insert into t (id, name, attr) values (2, 'A2', null);
insert into t (id, name, attr) values (3, 'A3', 'VALX');
insert into t (id, name, attr) values (4, 'A4', null);
insert into t (id, name, attr) values (5, 'A5', null);
insert into t (id, name, attr) values (6, 'A6', null);

create table r (
  id number(6),
  child_id number(6),
  parent_id number(6)
);

insert into r (id, child_id, parent_id) values (1, 6, 4);
insert into r (id, child_id, parent_id) values (2, 5, 4);
insert into r (id, child_id, parent_id) values (3, 4, 3);
insert into r (id, child_id, parent_id) values (4, 3, 1);
insert into r (id, child_id, parent_id) values (5, 2, 1);
1 голос
/ 14 февраля 2020

Вот как вы можете выполнить все это за один проход connect by - используя различные функции, доступные для этого типа запроса (включая флаг connect_by_isleaf и псевдостолбец connect_by_root):

select  connect_by_root(r.child_id) as id,
        connect_by_root(t.name)     as name,
        t.name                      as name_parent,
        t.attr                      as attribute_parent
from    r join t on r.child_id = t.id
where   connect_by_isleaf = 1
start   with r.child_id = 6
connect by prior r.parent_id = r.child_id and prior t.attr is null
;

        ID NAME       NAME_PARENT ATTRIBUTE_PARENT
---------- ---------- ----------- ----------------
         6 A6         A3          VALX   

Обратите внимание, что при этом все равно будет возвращаться null ATTRIBUTE_PARENT, если по всему дереву обойдется, не найдя предка с ненулевым ATTRIBUTE. Если на самом деле вы хотите что-то показать в выходных данных, только если у предка есть ненулевое значение ATTRIBUTE (и разрешить выходным данным не иметь строк, если такого предка нет), вы можете изменить выражение where на where t.attr is not null , Тем не менее, в большинстве случаев вам, вероятно, понадобится поведение, как я его кодировал.

Я использовал таблицы и данные, которые были опубликованы в ответе @TheImpaler (спасибо за высказывания create table и insert !)

Как я прокомментировал под его ответом: рекурсивное предложение with входит в стандарт SQL, поэтому оно имеет некоторые преимущества перед connect by. Однако, когда такую ​​же работу можно выполнить с connect by, стоит хотя бы протестировать ее таким же образом. Во многих случаях из-за многочисленных оптимизаций, которые Oracle придумывает со временем, connect by будет намного быстрее.

Одна из причин, по которой некоторые разработчики избегают connect by, заключается в том, что они не тратят время на изучить различные функции (например, те, которые я использовал здесь). На мой взгляд, это не очень хорошая причина.

...