具有2个表的递归CTE在Oracle / SQL中不起作用

时间:2018-07-07 11:42:28

标签: sql oracle

有两个表Departmentsubdepartment具有共同的ID。 我试图递归地获取直接和间接向AB报告的所有ID。 BC向AB报告,因此4,5,6间接向AB报告,直到获取最后一个ID。

尝试了下面的递归CTE查询,但仅获得第一级的结果。似乎recursive查询未执行。

我不确定查询中出了什么问题。有人可以帮助我发现错误。

部门

Name     id
AB          1
AB          2
AB          3
BC          4
BC          5
BC          6
CD          7
CD          8
EF          9
EF         10
EF         11

子部门

ID      Reporting
1
2
3         BC
4
5         CD
6
7
8         EF
9
10
11

查询:

With reportinghierarchy (Name, Id, Reporting, Level) As
(

--Anchor
Select A.name,A.id,reporting,0 from department A, subdepartment B
where A.id=B.id and A.name='AB'

Union All

--Recursive member
Select C.name,C.id,D.reporting, Level+1 from department C, subdepartment D
Inner Join  reportinghierarchy R
On (C.Name = R.reporting)
Where C.name != 'AB' and C.Id =D.id
And R.Reporting is not null
)
Select * from reportinghierarchy

当前输出:

Name Id  Reporting  Level
AB   1                0
AB   2                0
AB   3   BC           0

预期输出:

Name     id     Reporting  Level
AB        1                  0
AB        2                  0
AB        3       BC         0
BC        4                  1
BC        5       CD         1
BC        6                  1
CD        7                  2
CD        8       EF         2
EF        9                  3
EF       10                  3
EF       11                  3

2 个答案:

答案 0 :(得分:1)

嗯,“可怕的数据结构”浮现在脑海。此方法为每个“报告”名称获取一行以用于递归CTE部分。然后它将级别重新连接到原始数据。

with ds as (
      select d.name, d.id, sd.reporting
      from department d join
           subdepartment sd
           on d.id = sd.id
     ),
     nd as (
      select d.name, sd.reporting
      from ds
      where sd.reporting is not null
     ),
     cte as (
      select ds.name, nd.reporting, 0 as lev
      from nd
      where not exists (select 1 from nd nd2 where nd2.reporting = nd.name)
      union all
      select nd.name, nd.reporting, lev + 1
      from cte join
           nd
           on nd.name = cte.reporting 
    )
select ds.*, cte.lev
from ds join
     cte
     on ds.name = cte.name;

此外,学习使用正确的显式JOIN语法。数十年来,它一直是标准语法。

答案 1 :(得分:0)

您的原始查询实际上非常非常接近工作。它不起作用的原因是:

  1. 您将关键字LEVEL用作列名而没有引用它。在Oracle中,LEVEL具有特定的含义,并且在上下文之外使用它会使解析器无休止地头痛。我将其更改为LVL,效果很好。
  2. 在UNION的递归部分中,您混合了旧式和新式连接。这是一个巨大的问题,绝不应该做。使用“旧式”隐式连接,或使用“新式”显式连接。为了尽可能接近您的原始语言,我使用了隐式连接,但是良好的编码实践表明您应该一直使用显式连接。

更正后的查询是:

With reportinghierarchy (Name, Id, Reporting, lvl) As
(

--Anchor
Select A.name,A.id,reporting,0 from department A, subdepartment B
where A.id=B.id and A.name='AB'

Union All

--Recursive member
Select C.name,C.id,D.reporting, lvl+1 from department C, subdepartment D, reportinghierarchy R
Where C.name != 'AB' and C.Id =D.id and C.Name = R.reporting
And R.Reporting is not null
)
Select * from reportinghierarchy;

鉴于上述情况,返回了以下结果,这些结果似乎与您想要的结果相符:

NAME    ID  REPORTING   LVL
AB      1   (null)      0
AB      2   (null)      0
AB      3   BC          0
BC      4   (null)      1
BC      5   CD          1
BC      6   (null)      1
CD      7   (null)      2
CD      8   EF          2
EF      9   (null)      3
EF      10  (null)      3
EF      11  (null)      3

SQLFiddle here

好运。