如何选择层次结构集合? (与非等级数据混合等)

时间:2014-08-26 12:21:44

标签: sql oracle plsql hierarchy hierarchical-data

有桌子:

table schema and data

我需要显示以下内容:

| ID | PERSONID | MASTERID | CHILDID     | VALUE | DEPTHLEVEL |
---------------------------------------------------------------
| 1  | 3        |  78452   |  21456      |  100  |     1      |
| 2  | 3        |  21456   |             |  0    |     2      |
| 3  | 3        | 652314   | 417859      |  115  |     1      |
| 4  | 3        | 417859   |             |  0    |     2      |
| 5  | 4        | 998654   | 223655      |  300  |     1      |
| 6  | 4        | 223655   |             |  0    |     2      |
| 7  | 4        | 201302   |789654,441592|  200  |     1      |
| 8  | 4        | 789654   |             |  0    |     2      |
| 9  | 4        | 441592   |             |  0    |     2      |
| 10 | 5        | 999852   |             |  123  |     1      |

查看id为10的行,此行没有关系(childs),id为7的行有两个childs。

我需要退出(将值设为0)每个子/叶的值。

对于第1-9行,我尝试以下查询:

select v.* from
(
  select v.id, v.personid,
    case when level > 1
      then 0
    else
      v.value
    end thevalue,
    v.masterid, v.childid, level depthlevel
    from tmpsimpleexample v
    start with v.childid is not null
    connect by v.masterid = prior v.childid
) v
order by v.id

结果: Results from my query

查看id为7的行,8是有两个孩子的主人,我需要把它放在一行

这是第一个问题。

此外,我需要显示没有层次关系的数据(预期结果表中的id为10,图像表数据中为id 11)。

我认为我可以使用未被childid引用的masterid查询所有行,然后在第一个查询(上面)和查询之间建立联合,以搜索未被childid引用的所有master id。

使用未被childid引用的masterid搜索所有行的查询将向我显示没有关系的行和级别1的主行。

select id, personid, value thevalue, masterid, childid, 1 depthlevel
    from TMPSIMPLEEXAMPLE
    where masterid not in
      (select childid from TMPSIMPLEEXAMPLE where childid is not null)

Querying all rows with masterid not referenced by chilid

这里我可以做一个联合,结果将符合我的要求(除了主行的childid连接)。

select v.* from
(
  select v.id, v.personid,
    case when level > 1
      then 0
    else
      v.value
    end thevalue,
    v.masterid, v.childid, level depthlevel
    from tmpsimpleexample v
    start with v.childid is not null
    connect by v.masterid = prior v.childid
  union
  select id, personid, value thevalue, masterid, childid, 1 depthlevel
    from TMPSIMPLEEXAMPLE
    where masterid not in
      (select childid from TMPSIMPLEEXAMPLE where childid is not null)
) v
order by v.id

几乎是最终结果:

Almost the final result

但是知道我的真实桌面有成千上万的记录使这样的联盟成为一种好方法吗?

1 个答案:

答案 0 :(得分:0)

我已经采取了我认为您的源数据的样子:

| ID | PERSONID | MASTERID  | CHILDID | VALUE |
-----------------------------------------------
| 1  |        3 |    78452  | 21456   | 100   |
| 2  |        3 |    21456  |         |  -1   |
| 3  |        3 |    652314 | 417859  | 115   |
| 4  |        3 |    417859 |         |  -1   |
| 5  |        4 |    998654 | 223655  | 300   |
| 6  |        4 |    223655 |         |  -1   |
| 7  |        4 |    201302 | 441592  | 200   |
| 7  |        4 |    201302 | 789654  | 200   |
| 9  |        4 |    441592 |         |  -1   |
| 8  |        4 |    789654 |         |  -1   |
| 10 |        4 |    999852 |         | 123   |
-----------------------------------------------

以下查询可以获得所需的结果:

enter code here
select id, 
       personid, 
       masterid,
       listagg(childid, ',') within group (order by childid) childid, 
       -- Took a guess that all values for a personid were the same and didn't need to be aggregated...
       min(decode(depthlevel, 1, value, null)) value,
       min(depthlevel) depthlevel
from  (select v.*, level depthlevel
       from   tmpsimpleexample v
       connect by v.masterid = prior v.childid
       -- Trick here is to start with all of the desired starting conditions...
       start with not exists ( select 'X' from tmpsimpleexample v2 where v2.childid = v.masterid ))
group by id, personid, masterid;

如果 CHILDID 的排序很重要,则需要使用 TMPSIMPLEEXAMPLE 重新加入嵌套视图:

select v1.id, 
       v1.personid, 
       v1.masterid,
       listagg(v1.childid, ',') within group (order by v2.id) childid, 
       min(decode(depthlevel, 1, v1.value, null)) value,
       min(depthlevel) depthlevel
from  (select v.*, level depthlevel
       from   tmpsimpleexample v
       connect by v.masterid = prior v.childid
       start with not exists ( select 'X' from tmpsimpleexample v2 where v2.childid = v.masterid )) v1,
       tmpsimpleexample v2
-- Outer Join is important!
where  v1.childid = v2.masterid (+)
group by v1.id, v1.personid, v1.masterid;

这里真正的魔力是LISTAGGG功能。如果你还没有11g或更好(为什么不呢?!?),那么下面的文章可以指导你构建自己的聚合函数:

http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php