Oracle SQL 分层查询按顺序字段连接

Oracle SQL hierarical query connected by sequential field

我有一个这样的分层任务方案:

|-- task_1
|---- task_1_1
|------ task_1_1_1
|------ task_1_1_2
|---- task_1_2
|------ task_1_2_1
|------ task_1_2_2
|-- task_2
etc.

每个级别的任务数量可能会有所不同,因为此层次结构的深度可能会有所不同。

在数据库中,它们存储为一个序列:

name sequence parent_sequence project_id
task_1 1 -1 1
task_1_1 2 1 1
task_1_1_1 3 2 1
task_1_1_2 4 2 1
task_1_2 5 1 1
task_1_2_1 6 5 1
task_1_2_2 7 5 1
task_2 8 -1 1
task_b_1 1 -1 2
task_b_1_1 2 1 2
task_b_1_1_1 3 2 2

为了重现层次结构,我使用了这个查询

SELECT
    kid.name AS kid_name,
    parent.name AS parent_name
FROM
    task kid
    LEFT JOIN task parent ON parent.sequence = kid.parent_sequence 
                             AND parent.project_id = kid.parent_id

当我尝试使用分层查询时

SELECT
    task.name AS task_name,
    SYS_CONNECT_BY_PATH(task.name, '/') AS task_path
FROM
   task
START WITH task.parent_sequence = -1
CONNECT BY PRIOR task.sequence = task.parent_sequence 

它没有考虑条件 parent_id = parent_id

我尝试硬编码 WHERE parent_id = 1LEFT JOIN 以便能够在 CONNECT BY PRIOR 和其他组合中执行 AND kid.project_id = parent.project_id,但每次都会构建记录积分的层次结构。

匹配 task.sequence = task.parent_sequence,因此出现错误的重复项,例如 task_1/task_b_1_1

问题是多个记录可以包含相同的 parent_sequencesequence(因为它们是在一个 project_id 的范围内计算的)。

我无法触摸数据库原理图。

如何构建按 project_id 分组的分层查询?

干杯

您可以将 project_id 比较添加到 CONNECT BY 过滤器:

SELECT project_id,
       task.name AS task_name,
       SYS_CONNECT_BY_PATH(task.name, '/') AS task_path
FROM   task
START WITH task.parent_sequence = -1
CONNECT BY
      PRIOR task.sequence = task.parent_sequence 
AND   PRIOR project_id    = project_id
ORDER SIBLINGS BY project_id, sequence

其中,对于示例数据:

CREATE TABLE task (name, sequence, parent_sequence, project_id) AS
SELECT 'task_1',       1, -1, 1 FROM DUAL UNION ALL
SELECT 'task_1_1',     2,  1, 1 FROM DUAL UNION ALL
SELECT 'task_1_1_1',   3,  2, 1 FROM DUAL UNION ALL
SELECT 'task_1_1_2',   4,  2, 1 FROM DUAL UNION ALL
SELECT 'task_1_2',     5,  1, 1 FROM DUAL UNION ALL
SELECT 'task_1_2_1',   6,  5, 1 FROM DUAL UNION ALL
SELECT 'task_1_2_2',   7,  5, 1 FROM DUAL UNION ALL
SELECT 'task_2',       8, -1, 1 FROM DUAL UNION ALL
SELECT 'task_b_1',     1, -1, 2 FROM DUAL UNION ALL
SELECT 'task_b_1_1',   2,  1, 2 FROM DUAL UNION ALL
SELECT 'task_b_1_1_1', 3,  2, 2 FROM DUAL;

输出:

PROJECT_ID TASK_NAME TASK_PATH
1 task_1 /task_1
1 task_1_1 /task_1/task_1_1
1 task_1_1_1 /task_1/task_1_1/task_1_1_1
1 task_1_1_2 /task_1/task_1_1/task_1_1_2
1 task_1_2 /task_1/task_1_2
1 task_1_2_1 /task_1/task_1_2/task_1_2_1
1 task_1_2_2 /task_1/task_1_2/task_1_2_2
1 task_2 /task_2
2 task_b_1 /task_b_1
2 task_b_1_1 /task_b_1/task_b_1_1
2 task_b_1_1_1 /task_b_1/task_b_1_1/task_b_1_1_1

db<>fiddle here