2016-08-13 56 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应该只在“C”键的首次亮相被displayin。所以那些标有“*”的应该不会出现。

我知道我可以创建一个辅助查询,在这个辅助查询中我发现了这个双亲键,然后根据这个排除了行。 但是我不知道是否有最短的方法来做到这一点,与hiearchy本身一起工作....如果有一种方法可以知道一个密钥已经有另一个父母。 (因为这是一个视图,我需要在查询中这样做,而不是PL/SQL。)

在此先感谢。

+0

你是什么意思由“第一”发生?结果集中的行没有排序(它们就像篮子里的球),所以这个问题没有多大意义。结果集中的所有行都是同时生成的(甚至可能是通过矢量化处理实现的,而且在任何情况下都是逻辑上的)。所以,即使在您澄清“第一”的含义之后,您也不太可能一次完成此操作。 – mathguy

回答

2

我承认我一般是sql-server,所以这是一个想法,但它可能需要一些调整和语法帮助。但是如何添加一个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中添加父项,如下所示:PARTITION BY CHILD,PARENT。这将保留每个子父项的一个RANDOM副本;如果您想要特定的副本,则必须确定排序应为 – mathguy