Oracle SQL 使用 while 循环到 select 记录

Oracle SQL using while loop to select records

我正在尝试为存储地理位置数据的 table 编写 sql。 table 有 4 列 - 位置 ID、父 ID、显示名称和位置类型。父 ID 将 link 与同一 table 中的另一条记录的位置 ID,它可能为空。

Table Structure

location_id parent_id display_name location_type
1 null United States Country
2 1 Texas, United States States
3 2 Amarillo, Texas City
4 null Hong Kong Country

我想生成一个包含 3 列的报告:locationName、States、Country,逻辑如下:当记录有父记录时,根据其显示名称将其显示到 States/Country 列位置类型。但我不知道如何使用 oracle sql.

有人可以帮我解决这个问题吗?

enter image description here

locationName States Country
United States null United States
Texas, United States Texas, United States United States
Amarillo, Texas Texas, United States United States
Hong Kong null Hong Kong

逻辑如下:

while(record still have parent id){
    if(location type = "States"){
       States column value = record's display_name
    }
    if(location type = "Country"){
       Country column value = record's display_name
    }
}

通过递归查询会更容易,但在这种情况下,作为 3 中的连接级别,可以通过一系列 self-left 连接来完成。

注意下面使用的子查询。第一个子查询 s 在第 1 级派生 state。第二个子查询 c 在第 2 级派生 country。第三个 c1 派生城市的国家,并加入使用s,这将是第3级。然后使用case语句,您可以获得所需的结果。

https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=e22239082df9132b0c0790c8d1b8ff0e

select 
display_name as location_name
,case when 
    s.state_id is null and location_type='States' 
        then gl.display_name 
    else s.state_name
end as states
,case when
    c.country_id is null and location_type='Country' and gl.display_name is not null
        then gl.display_name
    when c.country_name is not null 
        then c.country_name
    else c1.country_name end as country
from gl
left join 
    (select location_id as state_id,parent_id as country_id,display_name as state_name
    from gl where location_type='States'
    ) s 
on gl.parent_id=s.state_id
left join 
    (select location_id as country_id,display_name as country_name
    from gl where location_type='Country'
    ) c
on gl.parent_id=c.country_id
left join 
    (select location_id as country_id,display_name as country_name
    from gl where location_type='Country'
    ) c1
on s.country_id=c1.country_id
order by gl.location_id

这是一种使用递归查询的方法。 Fiddle 借自 Utsav 的回答。

CONNECT_BY_ROOT 允许获取用于启动递归的行的属性,因此我们可以按其进行分组。 CASE returns NULL 如果不满足条件则被 MIN.

忽略
SELECT root_display_name,
       MIN(CASE WHEN location_type = 'States'  THEN display_name END) AS states,
       MIN(CASE WHEN location_type = 'Country' THEN display_name END) AS country
FROM
(
  SELECT CONNECT_BY_ROOT(location_id) AS root_location_id,
         CONNECT_BY_ROOT(display_name) AS root_display_name,
         t.*
  FROM t
  CONNECT BY PRIOR parent_id = location_id
)
GROUP BY root_location_id, root_display_name
ORDER BY root_location_id