查询以获取分层数据及其级别编号

时间:2018-01-22 21:49:03

标签: sql oracle oracle10g hierarchical-data recursive-query

我在Oracle 10G数据库中有一个包含以下字段的表

GOAL_ID
PARENT_GOAL_ID
GOAL_NAME

之类的数据
GOAL_ID PARENT_GOAL_ID   GOAL_NAME
1          null           GoalX
2            1            GoalY
3            1            GoalZ
4            3            GoalN

我需要一个查询,通过将关卡添加到GOAL_NAME来提供结果,如下面的

1     GoalX
1.1   GoalY
1.2   GoalZ
1.2.1 GoalN

子节点数可以扩展到任何范围。因此查询应该能够获得其级别编号

4 个答案:

答案 0 :(得分:2)

啊,递归!

对于每一行,查询需要跟踪(或者我应该说是)家谱,直到它到达Null parent_goal_id(最终父节点/根节点)并通过dot-deliminating goal_id来构建节点id一路走来,不管有多少级别。递归就是答案。

我们可以使用CTE直接使用CTE,这对于SQL来说非常优雅:)。这样做(我测试了它,结果如下):

WITH a (node, goal_id, parent_goal_id, goal_name) AS 
    (SELECT cast(goal_id AS varchar) + '' + cast('' AS varchar) AS node, *
        FROM tbl
        WHERE parent_goal_id IS NULL  --ultimate parent
        UNION ALL
        SELECT cast(a.node AS varchar) + '.' + cast(b.goal_id AS varchar) AS node, b.*
        FROM tbl b
        JOIN a ON b.parent_goal_id = a.goal_id
    )
SELECT * FROM a

结果 - 我为测试添加了更多行:

node       goal_id  parent_goal_id  goal_name
1          1        NULL            goalx
1.2        2        1               goaly
1.3        3        1               goalz
1.3.4      4        3               goaln
1.3.8      8        3               goald
1.3.4.6    6        4               goalb
1.3.4.6.7  7        6               goalc
1.2.5      5        2               goala 

我希望这会有所帮助。

答案 1 :(得分:2)

您可以使用recursive subquery factoring(来自11gR2)来完成层次结构并构建“级别”标签/字符串,使用分析查询来计算每次当前级别中的位置循环:

with rcte (root_id, label, goal_level, goal_id, goal_name) as (
  select goal_id,
    to_char(row_number() over (order by goal_id)),
    1,
    goal_id,
    goal_name
  from goals
  where parent_goal_id is null
  union all
  select r.root_id,
    r.label ||'.'|| row_number()
      over (partition by r.root_id, r.goal_level order by g.goal_id),
    r.goal_level + 1,
    g.goal_id,
    g.goal_name
  from rcte r
  join goals g
  on g.parent_goal_id = r.goal_id
)
select label, goal_name
from rcte
order by root_id, goal_level, goal_id;

锚成员获取根ID - 没有父项的那些 - 并在层次结构中将它们指定为级别1,同时通过分析函数对它们进行编号。

递归成员然后找到子项,增加级别,并将父项子项中的序号连接到标签字符串。

递归继续,直到没有更多的孩子。

在正常CTE中使用您的样本数据和一些额外内容进行演示:

with goals (GOAL_ID, PARENT_GOAL_ID, GOAL_NAME) as (
  select 1, null, 'GoalX' from dual
  union all select 2, 1, 'GoalY' from dual
  union all select 3, 1, 'GoalZ' from dual
  union all select 4, 3, 'GoalN' from dual
  union all select 5, null, 'GoalA' from dual
  union all select 6, 5, 'GoalB' from dual
  union all select 7, 6, 'GoalC' from dual
  union all select 8, 6, 'GoalD' from dual
)
, rcte (root_id, label, goal_level, goal_id, goal_name) as (
  select goal_id,
    to_char(row_number() over (order by goal_id)),
    1,
    goal_id,
    goal_name
  from goals
  where parent_goal_id is null
  union all
  select r.root_id,
    r.label ||'.'|| row_number()
      over (partition by r.root_id, r.goal_level order by g.goal_id),
    r.goal_level + 1,
    g.goal_id,
    g.goal_name
  from rcte r
  join goals g
  on g.parent_goal_id = r.goal_id
)
select label, goal_name
from rcte
order by root_id, goal_level, goal_id;

得到:

LABEL   GOAL_NAME
------- ---------
1       GoalX
1.1     GoalY
1.2     GoalZ
1.2.1   GoalN
2       GoalA
2.1     GoalB
2.1.1   GoalC
2.1.2   GoalD

答案 2 :(得分:1)

一个简单的答案是使用SQL> with test (goal_id, parent_goal_id, goal_name) as 2 (select 1, null, 'goalx' from dual union 3 select 2, 1, 'goaly' from dual union 4 select 3, 1, 'goalz' from dual union 5 select 4, 3, 'goaln' from dual 6 ) 7 select ltrim(sys_connect_by_path(level, '.'), '.') scbp, 8 goal_name 9 from test 10 connect by prior goal_id = parent_goal_id 11 start with parent_goal_id is null; SCBP GOAL_ ---------- ----- 1 goalx 1.2 goaly 1.2 goalz 1.2.3 goaln SQL>

tf.multinomial

我知道,SCBP并没有真正反映出您想要的输出,但是 - 这就是LEVEL伪列的结果。希望其他人能提供更好的解决方案。

答案 3 :(得分:1)

当您使用10g时,您还无法使用递归子查询因子分析(请参阅上一个答案),因此您会遇到hierarchical queries。 @Littlefoot已经展示了这种方法,但是根据有限的样本数据,使用层次结构数字似乎不是你想要的。

您可以使用常规(非递归)CTE根据当前父项为原始表中的每一行分配名义排名,然后对该CTE执行分层查询,使用生成的排名来构建'label'字符串:

with goals (GOAL_ID, PARENT_GOAL_ID, GOAL_NAME) as (
  select 1, null, 'GoalX' from dual
  union all select 2, 1, 'GoalY' from dual
  union all select 3, 1, 'GoalZ' from dual
  union all select 4, 3, 'GoalN' from dual
  union all select 5, null, 'GoalA' from dual
  union all select 6, 5, 'GoalB' from dual
  union all select 7, 6, 'GoalC' from dual
  union all select 8, 6, 'GoalD' from dual
),
cte as (
  select goal_id, parent_goal_id, goal_name,
    row_number() over (partition by parent_goal_id order by goal_id) as rn
  from goals
)
select ltrim(sys_connect_by_path(rn, '.'), '.') as label,
  goal_name
from cte
start with parent_goal_id is null
connect by parent_goal_id = prior goal_id;

在正常CTE中使用您的样本数据和一些额外内容进行演示:

LABEL                GOAL_
-------------------- -----
1                    GoalX
1.1                  GoalY
1.2                  GoalZ
1.2.1                GoalN
2                    GoalA
2.1                  GoalB
2.1.1                GoalC
2.1.2                GoalD

得到

etag