如何通过行层次结构进行SQL查询?

时间:2019-03-14 20:24:27

标签: sql oracle oracle11g

假设我在Oracle数据库中具有以下表结构,其中PARENT引用同一表中的另一行。

  id  | parent
  1   | null
  2   | 1
  3   | 2
  4   | 3
  5   | null
  6   | 5
  7   | 6

我该如何查询,以便每一行都让我知道序列的结尾。那就是我希望看到的最终结果:

  id  | end_parent
  1   | 4
  2   | 4
  3   | 4
  4   | 4
  5   | 7
  6   | 7
  7   | 7

3 个答案:

答案 0 :(得分:2)

以下查询显示了在Oracle 12c上测试所需的结果:

with
x (id, parent, origin, generation) as (
  select id, parent, id, 1 from my_table where parent is null
  union all
  select t.id, t.parent, x.origin, x.generation + 1
   from my_table t
  join x on t.parent = x.id
),
y (id, parent, generation, origin, rn) as (
  select id, parent, generation, origin, 
    row_number() over(partition by origin order by generation desc) as rn
  from x
),
z (id, origin) as (
  select id, origin from y where rn = 1
)
select x.id, z.id as end_parent
from x
join z on x.origin = z.origin
order by x.id

结果:

ID  END_PARENT
--  ----------
1   4
2   4
3   4
4   4
5   7
6   7
7   7

作为参考,这是我使用的数据:

create table my_table (
  id int,
  parent int
);

insert into my_table (id, parent) values (1, null);
insert into my_table (id, parent) values (2, 1);
insert into my_table (id, parent) values (3, 2);
insert into my_table (id, parent) values (4, 3);
insert into my_table (id, parent) values (5, null);
insert into my_table (id, parent) values (6, 5);
insert into my_table (id, parent) values (7, 6);

答案 1 :(得分:1)

使用connect by语法,并使用sys_connect_by_path创建路径。路径的最后一个值为end_parent。使用connect_by_root获取根父级的id。基于该组,将每个组的最大值作为end_parent。最后,将分组添加到每一行。

with id_paths as
(
    select
    t.id,
    substr(sys_connect_by_path(t.id,'>'),2,length(sys_connect_by_path(t.id,'>'))-1) as id_path,
    connect_by_root t.id as parent_group
    from my_table t
    connect by prior t.id = t.parent
    start with t.parent is null
),
end_parents as
(
    select 
    ip.parent_group,
    max(substr(ip.id_path, instr(ip.id_path,'>',-1)+1, length(ip.id_path))) as end_parent
    from id_paths ip
    group by ip.parent_group
)
select
ip.id,
ep.end_parent
from id_paths ip
inner join end_parents ep on ep.parent_group = ip.parent_group
order by ip.id;

输出

| ID | END_PARENT |
|----|------------|
|  1 |          4 |
|  2 |          4 |
|  3 |          4 |
|  4 |          4 |
|  5 |          7 |
|  6 |          7 |
|  7 |          7 |

SQL Fiddle Example

答案 2 :(得分:0)

根据此处https://docs.oracle.com/database/121/SQLRF/queries003.htm的Oracle文档,您应该能够做到:

SELECT      COALESCE(CONNECT_BY_ROOT parent, id) id, 
            id end_parent 
FROM        my_table
WHERE       CONNECT_BY_ISLEAF = 1
CONNECT BY  PRIOR id = parent
ORDER BY    id