PLSQL - 按先前连接(多对多)

时间:2013-07-02 14:11:42

标签: oracle plsql hierarchical-query

我有一个使用connect by语句来排序递归数据的查询。我遇到的问题是,偶尔会有一对多或多对一的关系,我不知道如何处理它。

  SELECT  *  
FROM (SELECT * FROM bdTable WHERE parentek = t_parKey)
START WITH source is null
CONNECT BY PRIOR target = source 

所以解释一下。我有一个源列和目标列。大约99%的时间这些都是一个唯一的ID。不幸的是,另外1%的时间在其中一列中有一组ID。此表是流程图类型工具的平面表示,因此存在可以具有许多输出和合并的拆分和决策,其可以具有许多输入。

为了在加载表的数据时处理这个问题,使用listagg函数将唯一ID连接在一起。所以我最终得到的目标值是'1254143,2356334,6346436,3454363,3462354,442356'。

因此,当我执行connect by语句时,它会完美地运行,直到遇到其中一个场景,此时它就会停止(当然是预期的)。
我想我可能会以某种方式使用IN或INSTR来使它工作,但还没有任何运气,我在网上找不到任何东西。

任何帮助都会受到赞赏.....

1 个答案:

答案 0 :(得分:0)

如果你想使用某些逻辑加入targetsource,基于每列中列出的一组值的交集,那么最可靠的方法是将字符串拆分为集合并对前一行的集合和当前行的集合进行操作以构建树。

在Oracle中,有许多技术可以从分离的字符串构建集合,其中一种是in this answer to another question

创建所需的集合类型:

create or replace type TIdList as table of varchar2(100);

您的案例中的内部选择将如下所示:

SELECT
  t.*,
  (
    cast(multiset( -- Convert set of values into collection
      select  -- Build list of values from separated string
          substr(
            source,
            decode( level, 1, 1, instr(source,',',1,level-1)+1 ),
            decode( instr(source,',',1,level), 0, length(source)+1, instr(source,',',1,level) )
              -
              decode( level, 1, 1, instr(source,',',1,level-1)+1 )
          ) code
      from dual
      start with source is not null
      connect by instr(source,',',1,level-1) > 0

    ) as TIdList )
  )                          source_id_list,
  (
    cast(multiset( -- Convert set of values into collection
      select  -- Build list of values from separated string
          substr(
            target,
            decode( level, 1, 1, instr(target,',',1,level-1)+1 ),
            decode( instr(target,',',1,level), 0, length(target)+1, instr(target,',',1,level) )
              -
              decode( level, 1, 1, instr(target,',',1,level-1)+1 )
          ) code
      from dual
      start with target is not null
      connect by instr(target,',',1,level-1) > 0
    ) as TIdList )
  )                          target_id_list
FROM bdTable t
WHERE t.parentek = t_parKey

因为我不知道哪个列(sourcetarget)包含单独的列表,所以我为每个列添加了列。

构建集合后,可以使用multiset operators和可用test functions来匹配目标与来源。例如。

with inner_query as (
  SELECT
    t.*,
    (
      cast(multiset( -- Convert set of values into collection
        select  -- Build list of values from separated string
            substr(
              source,
              decode( level, 1, 1, instr(source,',',1,level-1)+1 ),
              decode( instr(source,',',1,level), 0, length(source)+1, instr(source,',',1,level) )
                -
                decode( level, 1, 1, instr(source,',',1,level-1)+1 )
            ) code
        from dual
        start with source is not null
        connect by instr(source,',',1,level-1) > 0

      ) as TIdList )
    )                          source_id_list,
    (
      cast(multiset( -- Convert set of values into collection
        select  -- Build list of values from separated string
            substr(
              target,
              decode( level, 1, 1, instr(target,',',1,level-1)+1 ),
              decode( instr(target,',',1,level), 0, length(target)+1, instr(target,',',1,level) )
                -
                decode( level, 1, 1, instr(target,',',1,level-1)+1 )
            ) code
        from dual
        start with target is not null
        connect by instr(target,',',1,level-1) > 0
      ) as TIdList )
    )                          target_id_list
  FROM bdTable t
  WHERE t.parentek = t_parKey
)
select 
  level       lvl,
  tree_list.*
from
  inner_query tree_list
start with  
  source is null
connect by 
  nvl(cardinality(prior target_id_list MULTISET INTERSECT source_id_list),0) > 0

如果只有一列可以包含值列表,那么MEMBER OF构造很有用。