仅使用某些元素获取递归树"展开"

时间:2016-02-18 09:53:27

标签: oracle recursion

我们有一个包含自引用树结构的表(idparent_id)。让我们假设以下树结构:

+ 1
  + 2
  + 3
  + 4
    + 5
    + 6
    + 7
  + 8
+ 9
  + 10
  + 11

我想获取此数据以便在树中显示它。但只有某些记录扩大了。我目前正在使用以下查询:

SELECT ID, NAME "PATH"
  FROM GROUPS
  WHERE PRIOR ID IN(1, 4)
  CONNECT BY PARENT_ID = PRIOR ID
  START WITH PARENT_ID IS NULL;

这非常有效,并返回以下记录:

+ 1
  + 2
  + 3
  + 4
    + 5
    + 6
    + 7
  + 8
+ 9

问题是此查询返回扩展直接父级的所有记录,但不返回整个父链。因此,如果我们只是在扩展ID 4,则不应该返回记录5, 6, 7,因为1未展开。

到目前为止,我一直在尝试获取一个自定义列,该列指示元素是否已展开,该列计算是否显式扩展并且父元素也已展开。

SELECT ...
CASE WHEN (ID IN (4) AND PRIOR EXPANDED = 1) THEN 1 ELSE 0 end "EXPANDED"
...
WHERE "EXPANDED" = 1

这不起作用,因为我在WHERE语句和EXPANDED语句中都不能使用PRIOR EXPANDED别名。

是否有一种使用简单查询实现此目的的简单方法?

2 个答案:

答案 0 :(得分:3)

Oracle安装程序

CREATE TABLE hierarchy ( id, parent_id ) AS
  SELECT 1, NULL FROM DUAL UNION ALL
  SELECT 2, 1 FROM DUAL UNION ALL
  SELECT 3, 2 FROM DUAL UNION ALL
  SELECT 4, 1 FROM DUAL UNION ALL
  SELECT 5, 4 FROM DUAL UNION ALL
  SELECT 6, 5 FROM DUAL UNION ALL
  SELECT 7, NULL FROM DUAL UNION ALL
  SELECT 8, 7 FROM DUAL UNION ALL
  SELECT 9, 8 FROM DUAL UNION ALL
  SELECT 10, 9 FROM DUAL UNION ALL
  SELECT 11, 8 FROM DUAL;

查询 - IN子句让所有父母都解释

SELECT LPAD( '+ ', LEVEL*2, ' ' ) || id
FROM   hierarchy
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id
AND        parent_id IN ( 1, 2, 4, 5, 7, 8, 9 );

<强>输出

+ 1
  + 2
    + 3
  +4
    + 5
      + 6
+ 7
  + 8
    + 9
      + 10
    + 11

查询 - IN子句扩展了所有父级,除了4和8

SELECT LPAD( '+ ', LEVEL*2, ' ' ) || id
FROM   hierarchy
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id
AND        parent_id IN ( 1, 2, 5, 7, 9 );

<强>输出

+ 1
  + 2
    + 3
  +4
+ 7
  + 8

更新 - 显示叶节点

SELECT LPAD( '+ ', LEVEL*2, ' ' ) || id AS value,
       isleaf
FROM   (
  -- Find the leaves first (as if all parents are expanded)
  SELECT h.*,
         CONNECT_BY_ISLEAF AS isLeaf
  FROM   hierarchy h
  START WITH parent_id IS NULL
  CONNECT BY PRIOR id = parent_id
)
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id
AND        parent_id IN ( 1, 2, 4, 7, 9 );

<强>输出

VALUE                ISLEAF
---------------- ----------
+ 1                       0 
  + 2                     0 
    + 3                   1 
  + 4                     0 
    + 5                   0 
+ 7                       0 
  + 8                     0 

1表示该节点没有子节点,0表示该节点有子节点(即使它们可能未展开)。

答案 1 :(得分:0)

好的,只是看到了关于要求扩展整个父链的说明。以下是通过使用sys_connect_by_path构建该链,然后检查它是否为1(必须丢弃当前级别的最后一个节点)来完成此操作:

随着1和4的扩展,你得到:

  WITH hier as (
  SELECT 1 id, NULL parent_id, 1 expanded FROM DUAL UNION ALL
  SELECT 2, 1, 0 FROM DUAL UNION ALL
  SELECT 3, 2, 0 FROM DUAL UNION ALL
  SELECT 4, 1, 1 FROM DUAL UNION ALL
  SELECT 5, 4, 0 FROM DUAL UNION ALL
  SELECT 6, 4, 0 FROM DUAL UNION ALL
  SELECT 7, 4, 0 FROM DUAL UNION ALL
  SELECT 8, 1, 0 FROM DUAL UNION ALL
  SELECT 9, 1, 0 FROM DUAL UNION ALL
  SELECT 10, 9, 0 FROM DUAL UNION ALL
  SELECT 11, 9, 0 FROM DUAL )
SELECT LPAD( '+ ', lvl*2, ' ' ) || id
FROM (
      SELECT ID
           , parent_id 
           , level as lvl
           , sys_connect_by_path(expanded,'-')  as path_expanded 
        FROM hier
     CONNECT BY PARENT_ID = PRIOR ID
       START WITH PARENT_ID IS NULL
     )
WHERE --every node in the path from the parent is expanded.
     instr(substr(path_expanded,1,length(path_expanded)-2),'0') = 0
OR   parent_id is null     ; 


LPAD('+',LVL*2,'')||ID 
+ 1                    
  + 2                  
  + 4                  
    + 5                
    + 6                
    + 7                
  + 8                  
  + 9       

更改为在节点1取消展开,您将获得:

WITH hier as (
  SELECT 1 id, NULL parent_id, 0 expanded FROM DUAL UNION ALL
  SELECT 2, 1, 0 FROM DUAL UNION ALL
  SELECT 3, 2, 0 FROM DUAL UNION ALL
  SELECT 4, 1, 1 FROM DUAL UNION ALL
  SELECT 5, 4, 0 FROM DUAL UNION ALL
  SELECT 6, 4, 0 FROM DUAL UNION ALL
  SELECT 7, 4, 0 FROM DUAL UNION ALL
  SELECT 8, 1, 0 FROM DUAL UNION ALL
  SELECT 9, 1, 0 FROM DUAL UNION ALL
  SELECT 10, 9, 0 FROM DUAL UNION ALL
  SELECT 11, 9, 0 FROM DUAL )
SELECT LPAD( '+ ', lvl*2, ' ' ) || id
FROM (
      SELECT ID
           , parent_id 
           , level as lvl
           , sys_connect_by_path(expanded,'-')  as path_expanded 
        FROM hier
     CONNECT BY PARENT_ID = PRIOR ID
       START WITH PARENT_ID IS NULL
     )
WHERE --every node in the path from the parent is expanded.
     instr(substr(path_expanded,1,length(path_expanded)-2),'0') = 0
OR   parent_id is null ;



    LPAD('+',LVL*2,'')||ID 
    + 1