SQL多条件CTE递归

时间:2011-08-05 10:18:11

标签: sql recursion plsql common-table-expression recursive-query

我在数据库中有每个标识符的以下2条信息。控制它们的公司,以及控制它们的公司。

有些东西,2个表(忽略一些唯一标识符):

组织

orgid | org_immediate_parent_orgid
1     | 2
2     | 2
3     | 1
5     | 4

关系orgid - > org_immediate_parent_orgid表示公司有父母。在我看来它只有相关的org_immediate_parent_orgid - > orgid公司的母公司作为子公司

org_affiliations

orgid | affiliated_orgid
2     | 3
2     | 5
4     | 1
1     | 5

orgid - > affiliated_orgid是公司的附属公司

视觉表现应该是这样的:

Database representation

来自组织的红色关系,关于蓝色关系 org_affiliations

如果想要让2所有的所有公司(或2的子公司)拥有它们中的一部分:

select m.org_immediate_parent_orgid
,m.orgid
from oa.organizations m
where m.org_immediate_parent_orgid is not null
start with m.orgid in (Identifiers)
connect by nocycle prior  m.orgid=m.org_immediate_parent_orgid

返回

org_immediate_parent_orgid| orgid
1                         | 2
2                         | 2
3                         | 1

如果想要让所有公司都是2(或2的附属儿子)中有一部分:

select aff.orgid,aff.affiliated_orgid
from oa.org_affiliations aff
where aff.affiliated_orgid is not null
start with aff.orgid in(Identifiers)
connect by nocycle prior  aff.affiliated_orgid =aff.orgid

返回

orgid | affiliated_orgid
2     | 3
2     | 5

所有可能的关系:

  • Aff - > AFF
  • Aff - >子
  • 子 - > AFF
  • 子 - >子

我只找到Sub - > Sub(子公司的子公司),关系(2 - > 1和关系1 - > 3)和Aff - > Aff,关系(2 - > 3和关系2 - > 5)。它还需要我2个单独的查询。

如何在一个递归查询中提取所有可能的关系?

如果我传递标识符2,则应该可以返回以下内容:

Relation | Loop| orgid | children
Sub      | 1   | 2     |2
Sub      | 1   | 2     |1
Aff      | 1   | 2     |3
Aff      | 1   | 2     |5
Sub      | 2   | 1     |3
Aff      | 2   | 1     |5

在每个周期中,将检查每个标识符的子和附属关系。重复新生儿。

关于如何处理它的任何想法?

TL:DR: 2个表(子公司\附属机构),2个查询。想要从公司找到所有子公司和附属公司以及所有可能的子公司组合的单一查询。最终预期结果显示,只需按照图片表示。

编辑:正如Craig所评论,我修正了输出。

编辑2:在好帮助下,克雷格和鲍勃贾维斯让我继续遇到问题。

对于收集子公司,以下代码完美无瑕,输出结果如我所愿:

with
relations as
(
select orgid as children,org_immediate_parent_orgid as orgid,'Sub' as relation
from oa.organizations 
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

同样的AFF:

with
relations as
(
select affiliated_orgid as children, orgid as orgid,'Aff' as relation
from oa.org_affiliations    
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

但是不能“联合所有”?

with
relations as
(
select orgid as children,org_immediate_parent_orgid as orgid,'Sub' as relation
from oa.organizations

UNION ALL

select affiliated_orgid as children, orgid as orgid,'Aff' as relation
from oa.org_affiliations    
)
select distinct relation, level, orgid, children
from relations
where children is not null
start with orgid in (identifier)
connect by
nocycle prior children = orgid
order by 2,3,4

在sql开发人员中,我去检查“从7到400k的每次跳转解释计划和成本,只需添加”union all“。任何workarround?是CTE中的问题,在联合alL?

Bob Jarvis解决方案在我有comp-sub-sub-aff的情况下不会工作,或者它找到公司或所有附属公司的所有子公司

3 个答案:

答案 0 :(得分:2)

将其从评论转移到实际答案并提供我认为您需要的内容。

一些事情......一个是次要的......我相信你的第一个连接的标签是通过返回输出向后。另外,我不知道如何获得最终输出中的最后两行。 4是5的父母,不是孩子,为什么会出现?如果它不存在,那么最后一行也不会那样。

如果我正确阅读,您可以使用以下内容:

with
relations as
(
    select
        orgid,
        org_immediate_parent_orgid parent_id,
        'Sub' relation
    from
        organizations
    union all
    select
        orgid,
        null parent_id,
        'Aff' relation
    from
        org_affiliations
    where
        orgid not in (
            select affiliated_orgid
            from org_affiliations
        )
    union all
    select
        affiliated_orgid orgid,
        orgid parent_id,
        'Aff' relation
    from
        org_affiliations
)
select distinct relation, level, parent_id, orgid
from relations
where parent_id is not null
start with orgid = 2
connect by
    nocycle prior orgid = parent_id
order by 2,3,4

其中给出了以下输出:

RELATION|LEVEL|PARENT_ID|ORGID
Sub     |1    |2        |2
Sub     |2    |2        |1
Aff     |2    |2        |3
Aff     |2    |2        |5
Sub     |3    |1        |3
Aff     |3    |1        |5

最重要的是,两张桌子彼此相对设置(组织与父母有联系,附属机构与孩子有联系)。所以我在WITH子句中将它们制作成相同的格式,然后在组合集上使用connect by。

此外,由于某种原因,Oracle为第一个循环提供了与其他循环不同的级别,因为它是一个自引用。我假设如果这是一个问题,你可以为这种情况添加一些自定义逻辑。

答案 1 :(得分:1)

这是一个开始:

select 'SUB -> SUB' AS TYPE,
       m.orgid AS ORGID,
       m.org_immediate_parent_orgid AS PARENT_OR_AFF
  from organizations m
  where m.org_immediate_parent_orgid is not NULL
  start with m.orgid in (2)
  connect by nocycle prior m.orgid = m.org_immediate_parent_orgid
UNION ALL
select 'AFF -> AFF' AS TYPE,
       aff.orgid AS ORGID,
       aff.affiliated_orgid AS PARENT_OR_AFF
  from org_affiliations aff
  where aff.affiliated_orgid is not NULL
  start with aff.orgid IN (2)
  connect by nocycle prior aff.affiliated_orgid = aff.orgid;

如果您添加子查询以获得剩余的关系,那么您应该很高兴。

分享并享受。

答案 2 :(得分:0)

未测试。创建一个视图,以便首先简化从2个表中提取数据。

create view related(orgid, relatedid) as 
    select orgid, org_immediate_parent_orgid as relatedid from organizations
    union distinct
    select orgid, affiliated_orgid as relatedid from affiliated;

现在我们可以使用它来轻松地迭代地找到所有有趣的东西。

with recursive related_recursive(orgid, relatedid) as (
    select orgid, relatedid from related where relatedid = 2
    union
    select r.origid, rr.relatedid from related_recursive rr, related r
        where rr.orig = r.relatedid
) 
select orgid from related_recursive;

在这种情况下,您甚至可以删除related_recursive的relatedid列,但它很有用 如果你想删除或更改where部分并从related_recursive中选择*,则是必需的。

请记住,在主查询之前完全评估CTE,这样可能会引入很多页面 在主查询中最终过滤之前。