Oracle 中的递归查询,直到层次结构中的父级满足条件?

Recursive query in Oracle until a parent in the hierarchy meets a condition?

我有一个 table 如下所示:

ID  PARENT_ID  VALUE_ID
1   NULL       100
2   1          NULL
3   2          200
4   3          NULL 
5   1          300
6   2          NULL
7   6          400
8   7          500

而且我希望能够获取每个 ID 及其对应的 VALUE_ID。我想这样做的方式是,如果一行的 VALUE_ID 为 NULL,它会“继承”层次结构中位于其上方的第一个父级的 VALUE_ID,该父级具有 VALUE_ID 作为 NOT NULL。这就是查询结果:

ID  VALUE_ID
1   100
2   100      // -> inherits the value from PARENT_ID = 1;  
3   200
4   200      // -> inherits the value from PARENT_ID = 3;
5   300
6   100      // -> inherits the value from ID = 1, because the PARENT_ID = 2 also has VALUE_ID as NULL, so it goes deeper in the hierarchy;
7   400    
8   500

仅用一个递归或分层查询就可以完成这样的事情吗?还是可以在没有程序的情况下完成,也许?使用 CTE 或 CONNECT BY 子句?

您可以为此使用递归 CTE:

with cte(id, value_id, parent_value_id) as (
      select id, value_id, value_id as parent_value_id
      from t
      where value_id is not null
      union all
      select t.id, t.value_id, cte.parent_value_id
      from cte join
           t
           on t.parent_id = cte.id
      where t.value_id is null
     )
select *
from cte
order by id;

Here 是一个 db<>fiddle.

您可以使用相关的分层查询和 CONNECT_BY_ISLEAF 仅 return 一行:

SELECT id,
       parent_id,
       ( SELECT value_id
         FROM   table_name r
         WHERE  connect_by_isleaf = 1
         START WITH r.id = t.id
         CONNECT BY PRIOR parent_id = id
         AND PRIOR value_id IS NULL
       ) AS value_id
FROM   table_name t

因此,对于您的测试数据:

CREATE TABLE table_name ( ID, PARENT_ID, VALUE_ID ) AS
SELECT 1, NULL, 100  FROM DUAL UNION ALL
SELECT 2, 1,    NULL FROM DUAL UNION ALL
SELECT 3, 2,    200  FROM DUAL UNION ALL
SELECT 4, 3,    NULL FROM DUAL UNION ALL
SELECT 5, 1,    300  FROM DUAL UNION ALL
SELECT 6, 2,    NULL FROM DUAL UNION ALL
SELECT 7, 6,    400  FROM DUAL UNION ALL
SELECT 8, 7,    500  FROM DUAL

这输出:

ID | PARENT_ID | VALUE_ID
-: | --------: | -------:
 1 |      null |      100
 2 |         1 |      100
 3 |         2 |      200
 4 |         3 |      200
 5 |         1 |      300
 6 |         2 |      100
 7 |         6 |      400
 8 |         7 |      500

db<>fiddle here