表object_type_t
中的数据集如下所示:
OBJ_ID PARENT_OBJ OBJECT_TYPE OBJECT_DESC
--------- ------------ ------------- -----------------------
ES01 <null> ESTATE Bucks Estate
BUI01 ES01 BUILDING Leisure Centre
BUI02 ES01 BUILDING Fire Station
BUI03 <null> BUILDING Housing Block
SQ01 BUI01 ROOM Squash Court
BTR01 BUI02 ROOM Bathroom
AP01 BUI03 APARTMENT Flat No. 1
AP02 BUI03 APARTMENT Flat No. 2
BTR02 AP01 ROOM Bathroom
BDR01 AP01 ROOM Bedroom
BTR03 AP02 ROOM Bathroom
SHR01 BTR01 OBJECT Shower
SHR02 BTR02 OBJECT Shower
SHR03 BTR03 OBJECT Shower
在实际的等级术语中,看起来像这样:
ES01
|--> BUI01
| |--> SQ01
|--> BUI02
| |--> BTR01
|--> SHR01
=======
BUI03
|--> AP01
| |--> BTR02
| | |--> SHR02
| |--> BDR01
|--> AP02
|--> BTR03
|--> SHR03
我知道如何使用分层查询,例如CONNECT BY PRIOR
。我也知道如何通过connect_by_root
找到树的根。但我要做的是找到一个给定的&#34;等级&#34;一棵树(即不是根级别,而是给定对象的&#34; BUIDLING&#34;级别)。
例如,我希望能够查询层次结构中属于BUI01
的每个对象。
然后相反,给定一个对象ID,我希望能够查询该对象的关联(例如)ROOM object_id
。
如果我可以将每个OBJECT_TYPE
与给定的level
相关联,那么事情就会容易得多。但是从上面的示例中可以看出,BUILDING并不总是出现在层次结构中的第1级。
我最初的想法是将数据提取为中间表格格式(可能是物化视图),如下所示。这将允许我通过物化视图上的简单SQL查询找到我想要的数据:
OBJ_ID OBJECT_DESC ESTATE_OBJ BUILDING_OBJ ROOM_OBJ
--------- ---------------- ---------- ------------ ----------
ES01 Bucks Estate ES01
BUI01 Leisure Centre ES01 BUI01
BUI02 Fire Station ES01 BUI02
BUI03 Housing Block BUI03
SQ01 Squash Court ES01 BUI01 SQ01
BTR01 Bathroom ES01 BUI02 BTR01
AP01 Flat No. 1 BUI03
AP02 Flat No. 2 BUI03
BTR02 Bathroom BUI03 BTR02
BDR01 Bedroom BUI03 BDR01
BTR03 Bathroom BUI03 BTR03
SHR01 Shower ES01 BUI02 BTR01
SHR02 Shower BUI03 BTR02
SHR03 Shower BUI03 BTR03
但是(没有写PL / SLQ,我想避免),我还没有能够简明地构建一个能够实现这种表格格式的查询。
有谁知道我怎么做到这一点?可以吗?
解决方案必须在Oracle 12c中可执行。
另外:性能很重要,因为我的基础数据结构包含数十万行,结构可能非常深。所以更快的解决方案比慢速解决方案更受欢迎: - )
提前感谢您的帮助。
答案 0 :(得分:2)
如果我正确理解您的需要,也许您可以避免表格视图,直接查询您的表格;
假设您要查找属于BUI01
的所有对象,您可以尝试:
with test(OBJ_ID, PARENT_OBJ, OBJECT_TYPE, OBJECT_DESC) as
(
select 'ES01','','ESTATE','Bucks Estate' from dual union all
select 'BUI01','ES01','BUILDING','Leisure Centre' from dual union all
select 'BUI02','ES01','BUILDING','Fire Station' from dual union all
select 'BUI03','','BUILDING','Housing Block' from dual union all
select 'SQ01','BUI01','ROOM','Squash Court' from dual union all
select 'BTR01','BUI02','ROOM','Bathroom' from dual union all
select 'AP01','BUI03','APARTMENT','Flat No. 1' from dual union all
select 'AP02','BUI03','APARTMENT','Flat No. 2' from dual union all
select 'BTR02','AP01','ROOM','Bathroom' from dual union all
select 'BDR01','AP01','ROOM','Bedroom' from dual union all
select 'BTR03','AP02','ROOM','Bathroom' from dual union all
select 'SHR01','BTR01','OBJECT','Shower' from dual union all
select 'SHR02','BTR02','OBJECT','Shower' from dual union all
select 'SHR03','BTR03','OBJECT','Shower' from dual
)
select OBJECT_TYPE, OBJ_ID, OBJECT_DESC
from test
connect by prior obj_id = parent_obj
start with obj_ID = 'BUI01'
这考虑属于自己的BUI01
;如果你不想这样,你可以用非常简单的方式修改查询来切断起始值。
相反,假设您正在寻找SHR01
所在的房间,您可以尝试使用以下内容;它基本上是相同的递归思想,但按升序排列,而不是降序树:
with test(OBJ_ID, PARENT_OBJ, OBJECT_TYPE, OBJECT_DESC) as
(...
)
SELECT *
FROM (
select OBJECT_TYPE, OBJ_ID, OBJECT_DESC
from test
connect by obj_id = PRIOR parent_obj
start with obj_ID = 'SHR01'
)
WHERE object_type = 'ROOM'
在这两种情况下,您只扫描一次表,没有任何其他结构;这样,这就有机会足够快。
答案 1 :(得分:1)
所需的输出有3列,由对象类型决定。通常,这可以使用更多列进行扩展,每个列对应字段object_type
的每个可能值。即使使用给定的示例数据,也可以设想另外一列apartment_obj
。
为了使这个通用而不需要像对象类型值那样多次自联接表,可以使用CONNECT BY
和PIVOT
子句的组合:
SELECT *
FROM (
SELECT obj_id,
object_desc,
CONNECT_BY_ROOT obj_id AS pivot_col_value,
CONNECT_BY_ROOT object_type AS pivot_col_name
FROM object_type_t
-- skip the STARTS WITH clause to get all connected pairs
CONNECT BY parent_obj = PRIOR obj_id
)
PIVOT (
MAX(pivot_col_value) AS obj
FOR (pivot_col_name) IN (
'ESTATE' AS estate,
'BUILDING' AS building,
'ROOM' AS room
)
);
FOR ... IN
子句有一个硬编码的所需列的名称列表 - 没有_obj
后缀,因为它会在数据透视转换期间添加。
Oracle不允许动态检索此列表。注意:使用PIVOT XML
syntax时,此规则有一个例外,但是您可以在一列中获取XML输出,然后您需要解析该列。那将是相当低效的。
带有CONNECT BY
子句的子查询没有STARTS WITH
子句,这使得该查询将任何记录作为起始点并从那里生成后代。与CONNECT_BY_ROOT
选择一起,这允许生成所有连接的对的完整列表,其中层次结构中两者之间的距离可以是任何值。然后JOIN
匹配两者的较深层,因此您获得该节点的所有祖先(包括节点本身)。然后这些祖先就会转入专栏。
CONNECT BY
子查询也可以以向后遍历层次结构的方式编写。输出是相同的,但可能存在性能差异。如果是这样,我认为变化可以有更好的性能,但我没有在大型数据集上测试它:
SELECT *
FROM (
SELECT CONNECT_BY_ROOT obj_id AS obj_id,
CONNECT_BY_ROOT object_desc AS object_desc,
obj_id AS pivot_col_value,
object_type AS pivot_col_name
FROM object_type_t
-- Connect in backward direction:
CONNECT BY obj_id = PRIOR parent_obj
)
PIVOT (
MAX(pivot_col_value) AS obj
FOR (pivot_col_name) IN (
'ESTATE' AS estate,
'BUILDING' AS building,
'ROOM' AS room
)
);
请注意,在此变体中,CONNECT_BY_ROOT
返回该对的更深节点,因为相反的遍历。
您可以使用此查询:
SELECT t1.obj_id,
t1.object_desc,
CASE 'ESTATE'
WHEN t1.object_type THEN t1.obj_id
WHEN t2.object_type THEN t2.obj_id
WHEN t3.object_type THEN t3.obj_id
END estate_obj,
CASE 'BUILDING'
WHEN t1.object_type THEN t1.obj_id
WHEN t2.object_type THEN t2.obj_id
WHEN t3.object_type THEN t3.obj_id
END building_obj,
CASE 'ROOM'
WHEN t1.object_type THEN t1.obj_id
WHEN t2.object_type THEN t2.obj_id
WHEN t3.object_type THEN t3.obj_id
END room_obj
FROM object_type_t t1
LEFT JOIN object_type_t t2 ON t2.obj_id = t1.parent_obj
LEFT JOIN object_type_t t3 ON t3.obj_id = t2.parent_obj
答案 2 :(得分:0)
非常感谢@trincot的灵感,我已经制定了以下解决方案。它不是非常快速的生产数据,但它确实适用于任意深度的树。这不是动态的唯一方法是,必须提前选择要提取树的哪个级别,并且必须添加一个额外的列来捕获该数据。
原则是可以构建sys_connect_by_path
列,并使用正则表达式从那里提取所需的级别数据。
WITH base_data (obj_id, parent_obj, object_type, object_desc) AS (
SELECT 'ES01','','ESTATE','Bucks Estate' FROM dual union all
SELECT 'BUI01','ES01','BUILDING','Leisure Centre' FROM dual union all
SELECT 'BUI02','ES01','BUILDING','Fire Station' FROM dual union all
SELECT 'BUI03','','BUILDING','Housing Block' FROM dual union all
SELECT 'SQ01','BUI01','ROOM','Squash Court' FROM dual union all
SELECT 'BTR01','BUI02','ROOM','Bathroom' FROM dual union all
SELECT 'AP01','BUI03','APARTMENT','Flat No. 1' FROM dual union all
SELECT 'AP02','BUI03','APARTMENT','Flat No. 2' FROM dual union all
SELECT 'BTR02','AP01','ROOM','Bathroom' FROM dual union all
SELECT 'BDR01','AP01','ROOM','Bedroom' FROM dual union all
SELECT 'BTR03','AP02','ROOM','Bathroom' FROM dual union all
SELECT 'SHR01','BTR01','OBJECT','Shower' FROM dual union all
SELECT 'SHR02','BTR02','OBJECT','Shower' FROM dual union all
SELECT 'SHR03','BTR03','OBJECT','Shower' FROM dual ),
obj_hierarchy AS (
SELECT object_type, obj_id, object_desc, parent_obj, sys_connect_by_path(object_type||':'||obj_id,'/')||'/' r_path
FROM base_data
START WITH parent_obj IS null
CONNECT BY PRIOR obj_id = parent_obj
)
SELECT obj_id, object_desc,
CASE
WHEN instr(h.r_path, 'ESTATE:') > 1
THEN regexp_replace (h.r_path,'.*/ESTATE:([^/]+).*$', '\1')
ELSE ''
END obj_estate,
CASE
WHEN instr(h.r_path, 'BUILDING:') > 1
THEN regexp_replace (h.r_path,'.*/BUILDING:([^/]+).*$', '\1')
ELSE ''
END obj_building,
CASE
WHEN instr(h.r_path, 'ROOM:') > 1
THEN regexp_replace (h.r_path,'.*/ROOM:([^/]+).*$', '\1')
ELSE ''
END obj_room
FROM obj_hierarchy h