Требуется иерархический запрос Oracle, который возвращает только полные деревья для записей, где дочерние элементы соответствуют поисковой строке

Вот полный пример набора данных для этого запроса без обрезки деревьев, где ни один узел не соответствует строке поиска:

Уровень родительского идентификатора текста
---------------------------------------------
0      0          1 верхнего уровня
1      1          2 фу
1      1          3 еще
1      1          4 фу
0      0          7      toplevel2
1      7          8 второй уровень
1      7          9 другой

Мне нужно вернуть следующее, если пользователь выполняет поиск по 'foo':

0 0 1 верхнего уровня
1      1          2 фу
1      1          4 фу

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

Я новичок в Oracle (по крайней мере, недавно) и безуспешно пытался добавить к предложению CONNECT BY - всегда возвращает следующее:

1      1          2 фу
1      1          4 фу

PS - документы оракула и примеры на это подразумевают, что CONNECT_BY_ROOT будет захватывать предков, но все, что кажется, это сделать, это вернуть значения верхнего уровня (ROOT).

3 ответа

Чтобы пройти снизу вверх, важным битом является порядок значений после CONNECT BY PRIOR) order by используется для обратного вывода (как корень Foo) и distinct удаляет дублирующиеся значения верхнего уровня:

SELECT DISTINCT LEVEL, id, text
FROM t1
CONNECT BY PRIOR parent = id
START WITH text = 'foo'
ORDER BY LEVEL DESC

Примечание: если вы добавите дочерний элемент в foo и переключите идентификатор CONNCT BY PRIOR id = parent, вы получите дочерние элементы

если вы хотите увидеть всю иерархию, вы можете найти вершину дерева (ища строку без родителя)

SELECT id
FROM t1
WHERE parent=0
CONNECT BY PRIOR parent = id
START WITH text = 'foo'

затем используйте это в качестве идентификатора START WITH (и измените порядок обхода дерева во внешнем запросе, id = parent):

SELECT DISTINCT LEVEL, id, text
FROM t1
CONNECT BY PRIOR id = parent
START WITH id IN 
(
    SELECT id
    FROM t1
    WHERE parent=0
    CONNECT BY PRIOR parent = id
    START WITH text = 'foo'
)
ORDER BY LEVEL DESC

В зависимости от того, как вы используете колонку LEVEL (согласно моему комментарию).

Информация об иерархических запросах Oracle: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/queries003.htm

Это возвращает то, о чем вы просите, если LEVEL - псевдостолбец Oracle:

WITH t AS (SELECT 0 AS parent,
                  1 AS id,
                  'toplevel' AS text FROM DUAL
           UNION
           SELECT 1 AS parent,
                  2 AS id,
                  'foo' AS text FROM DUAL
           UNION
           SELECT 1 AS parent,
                  3 AS id,
                  'sumthin else' AS text FROM DUAL
           UNION
           SELECT 1 AS parent,
                  4 AS id,
                  'foo' AS text FROM DUAL
           UNION
           SELECT 0 AS parent,
                  7 AS id,
                  'toplevel2' AS text FROM DUAL
           UNION
           SELECT 7 AS parent,
                  8 AS id,
                  'secondlevel' AS text FROM DUAL
           UNION
           SELECT 7 AS parent,
                  9 AS id,
                  'anothersecondlevel' AS text FROM DUAL
          ) 
SELECT UNIQUE
       level,
       parent,
       id,
       text
  FROM t
 START WITH text = 'foo'
 CONNECT BY PRIOR parent = id
 ORDER BY parent;

Возвращает:

LEVEL PARENT ID TEXT
    2      0  1 toplevel
    1      1  2 foo     
    1      1  4 foo     

Если LEVEL - это столбец в вашей таблице, тогда:

WITH t AS (SELECT 0 AS tlevel,
                  0 AS parent,
                  1 AS id,
                  'toplevel' AS text FROM DUAL
           UNION
           SELECT 1 AS tlevel,
                  1 AS parent,
                  2 AS id,
                  'foo' AS text FROM DUAL
           UNION
           SELECT 1 AS tlevel,
                  1 AS parent,
                  3 AS id,
                  'sumthin else' AS text FROM DUAL
           UNION
           SELECT 1 AS tlevel,
                  1 AS parent,
                  4 AS id,
                  'foo' AS text FROM DUAL
           UNION
           SELECT 0 AS tlevel,
                  0 AS parent,
                  7 AS id,
                  'toplevel2' AS text FROM DUAL
           UNION
           SELECT 1 AS tlevel,
                  7 AS parent,
                  8 AS id,
                  'secondlevel' AS text FROM DUAL
           UNION
           SELECT 1 AS tlevel,
                  7 AS parent,
                  9 AS id,
                  'anothersecondlevel' AS text FROM DUAL
          ) 
SELECT UNIQUE
       tlevel,
       parent,
       id,
       text
  FROM t
 START WITH text = 'foo'
 CONNECT BY PRIOR parent = id
 ORDER BY parent;

Возвращает:

TLEVEL PARENT ID TEXT
     0      0  1 toplevel
     1      1  2 foo     
     1      1  4 foo     

Надеюсь, поможет...

Мой взгляд на часть "Иерархический запрос Oracle, который возвращает только полные деревья":

SELECT Parent_ID    
     , Child_ID 
     , INITIAL_Parent_ID
     , child_level
     , INITIAL_Parent_ID || children_CHAIN AS CONNECTION_CHAIN
FROM ( SELECT CONNECT_BY_ROOT Parent_ID AS INITIAL_Parent_ID
            , Parent_ID
            , Child_ID
            , LEVEL AS child_level
            , SYS_CONNECT_BY_PATH(Child_ID, '/') AS children_chain
       FROM REF_DECOMMISSIONS
       CONNECT BY NOCYCLE Parent_ID = PRIOR Child_ID
     )
WHERE INITIAL_Parent_ID NOT IN (SELECT Child_ID FROM REF_DECOMMISSIONS)
;

Чтобы отфильтровать деревья с деревьями, содержащими определенных потомков, вам нужно добавить еще одно условие в последний WHERE для дальнейшей фильтрации INITIAL_Parent_ID. Запрос станет:

WITH ROOT_TREES AS
( SELECT Parent_ID  
       , Child_ID   
       , INITIAL_Parent_ID
       , child_level
       , INITIAL_Parent_ID || children_CHAIN AS CONNECTION_CHAIN
  FROM ( SELECT CONNECT_BY_ROOT Parent_ID AS INITIAL_Parent_ID
              , Parent_ID
              , Child_ID
              , LEVEL AS child_level
              , SYS_CONNECT_BY_PATH(Child_ID, '/') AS children_chain
         FROM REF_DECOMMISSIONS
         CONNECT BY NOCYCLE Parent_ID = PRIOR Child_ID
       )
  WHERE INITIAL_Parent_ID NOT IN (SELECT Child_ID FROM REF_DECOMMISSIONS)
)
SELECT * 
  FROM root_trees 
WHERE INITIAL_Parent_ID IN (SELECT INITIAL_Parent_ID 
                              FROM root_trees 
                             WHERE Child_ID = 123)
;
Другие вопросы по тегам