检索每个元素的MASTER父级

时间:2018-11-20 10:04:19

标签: oracle parent-child hierarchical-data recursive-query

我有一个简单的查询:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION
from DEPARTMENT dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

query

这将返回有关departments的信息,其中某些行具有相同的DEP_IDDEP_NAMEPARENT_DEP_NAME。这是预期的结果,因为某些员工属于同一部门,但拥有不同的SITE_LOCATION

我要添加另一列MASTER_PARENT_ID和MASTER父项的DEP_ID

我称为主父的是被称为PARENT_DEP_ID但实际上不存在的主父(DEP_ID列中缺少值)。

因此,所有这些行实际上应具有等于MASTER_PARENT_ID的{​​{1}}值。这只是一个示例数据,但实际上许多行将具有不同的DEP_2000。并非所有的值都相同。

为此,对于每一行,我需要执行一个递归查询以遍历树,直到找到没有任何匹配MASTER_PARENT_ID的{​​{1}}值。 / p>

我试图阅读并理解Oracle文档和示例,但找不到适用于我的案例的工具。是否应该使用PARENT_DEP_ID之类的东西来执行这种递归功能?

SQL通常不是我的首选,甚至不是Oracle。我找不到解决方法。

谢谢

2 个答案:

答案 0 :(得分:1)

您可以使用CONNECT_BY_ISLEAF伪列来查找层次结构树的叶子,然后在开始导航树时使用CONNECT_BY_ROOT( ... )函数来获取值:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE DEPARTMENT( DEP_ID, DEP_NAME, PARENT_DEP_ID ) AS
SELECT 'DEP_2000', 'Dep0', NULL       FROM DUAL UNION ALL
SELECT 'DEP_2400', 'Dep1', 'DEP_2000' FROM DUAL UNION ALL
SELECT 'DEP_2410', 'Dep2', 'DEP_2400' FROM DUAL UNION ALL
SELECT 'DEP_2420', 'Dep3', 'DEP_2400' FROM DUAL;

查询1

SELECT CONNECT_BY_ROOT( DEP_ID )        AS DEP_ID,
       CONNECT_BY_ROOT( DEP_NAME )      AS DEP_NAME,
       CONNECT_BY_ROOT( PARENT_DEP_ID ) AS PARENT_DEP_ID,
       DEP_ID                           AS MASTER_PARENT_DEP_ID
FROM   DEPARTMENT
WHERE  CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR PARENT_DEP_ID = DEP_ID

Results

|   DEP_ID | DEP_NAME | PARENT_DEP_ID | MASTER_PARENT_DEP_ID |
|----------|----------|---------------|----------------------|
| DEP_2000 |     Dep0 |        (null) |             DEP_2000 |
| DEP_2400 |     Dep1 |      DEP_2000 |             DEP_2000 |
| DEP_2410 |     Dep2 |      DEP_2400 |             DEP_2000 |
| DEP_2420 |     Dep3 |      DEP_2400 |             DEP_2000 |

注意:通过遍历每个元素的树直到根,而不是反向,您不需要START WITH子句,也不需要单独的查询来查找根。

如果没有DEP_2000行(只需将DEP_ID更改为PARENT_DEP_ID),这甚至可以工作:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE DEPARTMENT( DEP_ID, DEP_NAME, PARENT_DEP_ID ) AS
--SELECT 'DEP_2000', 'Dep0', NULL       FROM DUAL UNION ALL
SELECT 'DEP_2400', 'Dep1', 'DEP_2000' FROM DUAL UNION ALL
SELECT 'DEP_2410', 'Dep2', 'DEP_2400' FROM DUAL UNION ALL
SELECT 'DEP_2420', 'Dep3', 'DEP_2400' FROM DUAL;

查询1

SELECT CONNECT_BY_ROOT( DEP_ID )        AS DEP_ID,
       CONNECT_BY_ROOT( DEP_NAME )      AS DEP_NAME,
       CONNECT_BY_ROOT( PARENT_DEP_ID ) AS PARENT_DEP_ID,
       PARENT_DEP_ID                    AS MASTER_PARENT_DEP_ID
FROM   DEPARTMENT
WHERE  CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR PARENT_DEP_ID = DEP_ID

Results

|   DEP_ID | DEP_NAME | PARENT_DEP_ID | MASTER_PARENT_DEP_ID |
|----------|----------|---------------|----------------------|
| DEP_2400 |     Dep1 |      DEP_2000 |             DEP_2000 |
| DEP_2410 |     Dep2 |      DEP_2400 |             DEP_2000 |
| DEP_2420 |     Dep3 |      DEP_2400 |             DEP_2000 |

答案 1 :(得分:0)

如果部门表中根本不存在主部门ID-例如,由于没有直接雇员,它就不会出现在结果中-那么您可以使用分层查询来获取根父级对于每个单位:

select dep_id, dep_name, parent_dep_id,
  connect_by_root(parent_dep_id) as master_parent_id
from department
connect by parent_dep_id = prior dep_id
start with parent_dep_id in (
  select parent_dep_id from department d1
  where not exists (
    select *
    from department d2
    where d2.dep_id = d1.parent_dep_id
  )
);

DEP_ID   DEP_NAME        PARENT_D MASTER_P
-------- --------------- -------- --------
DEP_2400 Department 2400 DEP_2000 DEP_2000
DEP_2410 Department 2410 DEP_2400 DEP_2000
DEP_2420 Department 2420 DEP_2400 DEP_2000

,然后将其用作主查询中的内联视图,而不是直接引用该表:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION,
dep.MASTER_PARENT_ID
from (
  select dep_id, dep_name, parent_dep_id,
    connect_by_root(parent_dep_id) as master_parent_id
  from department
  connect by parent_dep_id = prior dep_id
  start with parent_dep_id in (
    select parent_dep_id from department d1
    where not exists (
      select *
      from department d2
      where d2.dep_id = d1.parent_dep_id
    )
  )
) dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

DEP_ID   DEP_NAME        PARENT_D SITE_LO MASTER_P
-------- --------------- -------- ------- --------
DEP_2400 Department 2400 DEP_2000 SITE_01 DEP_2000
DEP_2400 Department 2400 DEP_2000 SITE_02 DEP_2000
DEP_2410 Department 2410 DEP_2400 SITE_01 DEP_2000
DEP_2410 Department 2410 DEP_2400 SITE_02 DEP_2000
DEP_2420 Department 2420 DEP_2400 SITE_01 DEP_2000
DEP_2420 Department 2420 DEP_2400 SITE_02 DEP_2000

虽然DEP_2000实际上存在,并且没有父代,这似乎更自然。如果确实如此,则内联视图会更简单:

SELECT DISTINCT 
dep.DEP_ID,
dep.DEP_NAME,
dep.PARENT_DEP_ID,
emp.SITE_LOCATION,
dep.MASTER_PARENT_ID
from (
  select dep_id, dep_name, parent_dep_id,
    connect_by_root(dep_id) as master_parent_id
  from department
  connect by parent_dep_id = prior dep_id
  start with parent_dep_id is null
) dep
INNER JOIN EMPLOYEE emp ON dep.DEP_ID = emp.DEP_ID
ORDER BY dep.DEP_ID;

请注意,除了start with子句只是在寻找一个空的父级外,connect_by_root()现在也在查看dep_id而不是parent_dep_id(该值为空) )。