如何在 oracle 11g 中获取分层连接数据的根

How to get root of hierarchial joined data in oracle 11g

我的架构如下所示。

我有等级区:

create table district(
  id integer primary key,
  name varchar2(32),
  parent_id integer references district(id)
)

以及区内房屋:

create table house(
  id integer primary key,
  name varchar2(32),
  district_id integer references district(id)
)

house.district_id 总是district 的层次结构的底部。我如何 select district 层次结构的 root 的每个房屋以及 idname

目前我正在使用两个子查询,但感觉不对:

select 
  h.id, 
  h.name,
  (
    select id from district
     where parent_id is null
     start with id = house.district_id
   connect by parent_id = id
  ) as district_id,
  (
    select name from district
     where parent_id is null
     start with id = house.district_id
   connect by parent_id = id
  ) as district_name
from house;

Oracle 版本为 11g 第 2 版。

示例数据: 地区

+-------------------+
| id name parent_id |
+-------------------+
| 1 'one' NULL      |
| 2 'two' 1         |
| 3 'three' 3       |
+-------------------+

房子

id name district_id
1 'h1' 3
2 'h2' 3
3 'h3' 3

期望的输出:

+------------------------------------+
| id name district_id, district_name |
+------------------------------------+
| 1 'h1' 1 'one'                     |
| 2 'h2' 1 'one'                     |
| 3 'h3' 1 'one'                     |
+------------------------------------+

而不是有一个(或两个)相关的子查询,您可以使用 connect_by_root() 获取所有地区 ID 及其根 - 然后加入:

with d (root_id, root_name, id) as (
  select connect_by_root(id), connect_by_root(name), id
  from district
  start with parent_id is null
  connect by parent_id = prior id
)
select
  h.id, 
  h.name,
  d.root_id,
  d.root_name
from house h
join d on d.id = h.district_id;

db<>fiddle

您也可以使用递归 CTE 而不是分层查询。

您可以使用connect_by_root获取所有根分区sub-districts和connect_by_isleaf仅在数据中保留底层分区

select h.id, h.name, d.root_id district_id, d.root_name district_name
from house h
inner join (
    select id, root_id, root_name from (
        select id, connect_by_root(id) root_id, connect_by_root(name) root_name, connect_by_isleaf is_leaf
        from district
        start with parent_id is null
        connect by prior id = parent_id
    )
    where is_leaf = 1
) d
on (h.district_id = d.id)

fiddle

我喜欢为此使用递归 with 子句。您正在使用的 Oracle 起始版本 11gR2 支持此功能。我发现与 connect by 查询相比,学习这种新语法是值得的,因为:

  • 它基于标准SQL规范(所有其他主要数据库都在with子句中支持递归)
  • 它比 Oracle-specific 方法
  • 更灵活

考虑:

with cte (id, parent_id, root_id, root_name) as (
    select id, parent_id, id as root_id, name as root_name
    from district
    where parent_id is null
    union all
    select d.id, d.parent_id, c.root_id, c.root_name
    from   cte c
    inner join district d on d.parent_id = c.id
) search depth first by id set order1
select h.id, h.name, c.root_id, c.root_name
from house h
inner join cte c on c.id = h.district_id