2016-08-13 7 views
1

Я работаю с иерархическими данными, в которых есть несколько ключей, у которых есть двойные или тройные родители.Иерархия Oracle с общими членами (двойные родительские элементы)

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

WITH HER(CHILD, PARENT) AS (
    SELECT 'A' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'B' AS CHILD, 'A' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'A' AS PARENT FROM DUAL UNION 
    SELECT 'D' AS CHILD, 'C' AS PARENT FROM DUAL UNION 
    SELECT 'E' AS CHILD, 'D' AS PARENT FROM DUAL UNION 
    SELECT 'F' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'G' AS CHILD, 'F' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'G' AS PARENT FROM DUAL UNION --<<--- shared 
    SELECT 'H' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'B' AS CHILD, 'H' AS PARENT FROM DUAL UNION --<<--- shared 
    SELECT 'X' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'Y' AS CHILD, 'X' AS PARENT FROM DUAL UNION 
    SELECT 'Z' AS CHILD, 'Y' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'Z' AS PARENT FROM DUAL  --<<--- shared 
    ) 
    SELECT 
     LPAD(' ', 7*(LEVEL-1),' ')||CHILD||' - '||PARENT 
    FROM HER 
    START WITH PARENT IS NULL 
    CONNECT BY PRIOR CHILD = PARENT 

;

Результат от этого запроса, но это не совсем то, что я пытаюсь получить:

---------- 
A - 
     B - A 
     C - A 
       D - C 
        E - D 
F - 
     G - F 
       C - G 
        *D - C* 
          *E - D* 
H - 
     B - H 
X - 
     Y - X 
       Z - Y 
        C - Z 
          *D - C* 
            *E - D* 

Ряды D-C и E-D должно быть просто displayin в первом появлении ключа «C». Поэтому те, которые отмечены знаком «*», не должны отображаться.

Я знаю, что я мог бы просто создать вторичный запрос, в котором я вижу эти двойные родительские ключи, а затем исключаю строки на основе этого. Но я задаюсь вопросом, существует ли самый короткий способ сделать это, работая с самим hiearchy .... если есть способ узнать, что у ключа уже есть другой родитель. (Поскольку это идет в представлении, мне нужно сделать это в запросе, а не в PL/SQL.)

Заранее спасибо.

+0

Что вы подразумеваете под «первым» случаем? Строки в результирующем наборе не упорядочены (они похожи на шарики в корзине), поэтому вопрос не имеет большого смысла. Все строки в результирующем наборе генерируются одновременно (возможно, даже физически с помощью векторизованной обработки и в любом случае логически). Поэтому даже после того, как вы уточните, что вы подразумеваете под «первым», маловероятно, что вы можете сделать это за один проход. – mathguy

ответ

2

Я признаю, что я, как правило, sql-сервер, так что это мысль для вас, но может потребоваться помощь по настройке и синтаксису. Но как насчет добавления ROW_NUMBER(), разделенного на уровне Child, и добавления второго условия в предложение connect by, чтобы ограничить рекурсию. Может, что-то вроде этого?

WITH HER(CHILD, PARENT) AS (
    SELECT 'A' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'B' AS CHILD, 'A' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'A' AS PARENT FROM DUAL UNION 
    SELECT 'D' AS CHILD, 'C' AS PARENT FROM DUAL UNION 
    SELECT 'E' AS CHILD, 'D' AS PARENT FROM DUAL UNION 
    SELECT 'F' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'G' AS CHILD, 'F' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'G' AS PARENT FROM DUAL UNION --<<--- shared 
    SELECT 'H' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'B' AS CHILD, 'H' AS PARENT FROM DUAL UNION --<<--- shared 
    SELECT 'X' AS CHILD, null AS PARENT FROM DUAL UNION 
    SELECT 'Y' AS CHILD, 'X' AS PARENT FROM DUAL UNION 
    SELECT 'Z' AS CHILD, 'Y' AS PARENT FROM DUAL UNION 
    SELECT 'C' AS CHILD, 'Z' AS PARENT FROM DUAL  --<<--- shared 
    ) 

    , HERChildRowNum(CHILD, PARENT, ChildRowNum) AS (

     SELECT 
      CHILD, 
      PARENT, 
      ROW_NUMBER() OVER (PARTITION BY CHILD ORDER BY (SELECT 0)) as ChildRowNum 
     FROM 
      HER 
    ) 


    SELECT 
     LPAD(' ', 7*(LEVEL-1),' ')||CHILD||' - '||PARENT 
    FROM HERChildRowNum 
    START WITH PARENT IS NULL 
    CONNECT BY PRIOR CHILD = PARENT AND (PRIOR ChildRowNum = ChildRowNum OR ChildRowNum>1) 
+0

Впечатляющий !, спасибо .... совсем близко .... но это решение также исчезнет с строк C-G и C-Z, и они должны появиться в отчете. Просто их дети должны быть выключены .... :( –

+0

@CraigStevensson - это (почти) правильное решение в Oracle, но я думал, что ваше требование было специально избегать «вторичного запроса». Это решение (и, возможно, любое другое возможное решение) использует вторичный запрос. Надеясь, что вы в порядке с этим (видимо, вы есть). – mathguy

+0

@CraigStevensson - чтобы исправить это решение для Oracle, в определении ROW_NUMBER() измените ORDER BY (SELECT 0) на просто ORDER BY 0 (или ORDER BY NULL), а в PARTITION BY CHILD добавьте PARENT следующим образом: PARTITION BY CHILD, PARENT. Это будет содержать одну RANDOM-копию каждого родителя-ребенка, и если вы хотите получить конкретную информацию, вы должны определить, какой заказ должен быть (используйте его в ЗАКАЗЕ BY). – mathguy