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

Sql 查询以获取分层数据及其级别号,sql,oracle,oracle10g,hierarchical-data,recursive-query,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 我需要一个查询,它将通过在目标名称前

我在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
我需要一个查询,它将通过在目标名称前面加上level来给出结果,如下所示

1     GoalX
1.1   GoalY
1.2   GoalZ
1.2.1 GoalN

子节点的数量可以任意扩展。因此,查询应该能够获得其级别编号

一个简单的答案是使用
SYS\u CONNECT\u BY\u PATH

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>
我知道,SCBP并不能真正反映您所期望的输出,但是-这就是您使用LEVEL伪列得到的结果。希望其他人能提供更好的解决方案。

Ahhh,递归! 对于每一行,查询需要向上(或者我应该说向下)跟踪家族树,直到它到达一个空的父级\目标\ id(最终父级/根节点),并通过点删除目标\ id来建立一个节点id,而不管有多少级别。递归就是答案

我们可以使用CTE直接使用SQL来实现这一点,这与SQL的特点不同,几乎是优雅的:)。这就是诀窍(我测试了它,结果如下):

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

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 
我希望这会有所帮助。

您可以使用(从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(没有父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;
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;
其中包括:

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

由于您使用的是10g,所以还不能使用递归子查询分解(请参见前面的答案),所以您只能使用@Littlefoot已经展示了这种方法,但使用的是层次结构级别的数字,基于有限的样本数据,这似乎不是您想要的

您可以使用常规(非递归)CTE根据当前父级为原始表中的每一行分配一个标称排名,然后对该CTE执行分层查询,使用这些生成的排名构建“标签”字符串:

with 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中使用您的样本数据和一些附加数据进行演示:

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;
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;
哪一个

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

你可能是对的@AlexPoole,据说是专门为数据库管理员服务的。我想R Khan会在那里找到帮助。你能添加更多的样本数据和结果来澄清“level”字符串应该如何生成吗?也要用文字解释一下吗?对不起,我需要用Oracle我正在运行SQL Server,所以我无法测试它,但我认为这仍然很接近。您是否收到“列别名列表”错误或其他错误?@RKhan-我正在运行SQL Server,因此无法测试它,但我认为这仍然很接近。您是否收到“列别名列表”错误或其他错误?#针对ORACLE更新#@RKhan,我更新了查询以包含ORACLE的显式列列表。看看这是否能解决问题。对于Oracle,在转换为varchar时需要给出一个大小(实际上应该是varchar2);连接运算符是
|
而不是
+
。(我们对这个问题的解释不同;只有OP可以告诉我们,我们中的哪一个得到了正确的结果,*8-)哦。。。刚刚注意到问题正文中提到的10g(但不是标签)。所以这种类型的递归不适用于OP。我会留下这个,因为它可能对其他人有用。