两个节点之间的完整路径循环结构pl / sql

时间:2013-05-02 09:19:10

标签: oracle plsql plsqldeveloper

我有一个输入数据,包括ID,prev,current和next节点(未排序) 我必须在每个ID的第一页和最后一页之间找到一条路径,它覆盖了所有遍历的节点 例如:如果我的输入数据如下:
第一列是ID,第二列是prev_node,第三列是当前节点,第四列是下一个节点 对于起始值,Prev_node将为空,而对于最后一个值

,下一个节点将为空

输入

id  prev  current  next     
------------------------
1    a      b       c  
1    a      e       f  
1    a      b       g  
1    a      b       o    
1    b      c       d  
1    b      g       h  
1    b      o       p  
1    c      d       a  
1    c      b       g  
1    d      a       e  
1    e      f       e  
1    e      f       f  
1    f      e       f  
1    f      f       f  
1    f      f       a  
1    f      a       b  
1    g      h       i  
1    h      i       j   
1    h      j       i  
1    i      j       i  
1    i      i       k  
1    i      k       l  
1    j      i       i  
1    k      l       m  
1    l      m       n  
1    l      n       a  
1    m      n       a  
1    n      a       b  
1    o      p       q  
1    p      q       r  
1    q      r       s  
1    r      s       t  
1    s      t       u  
1    t      u       v  
1    u      v       w  
1    v      w       x  
1    w      x         
1           a       b  

输出应该是当前节点的路径,如 -

ID    current  
-------------
1       a  
1       b  
1       c  
1       d  
1       a  
1       e  
1       f  
1       e  
1       f  
1       f  
1       f  
1      a  
1      b  
1      b  
1      g  
1      h  
1      i  
1      j  
1      j  
1      i  
1      i   
1      k  
1      l  
1      m  
1      n  
1      n  
1      a  
1      b  
1      o  
1      p  
1      q  
1      r  
1      s  
1      t  
1      u  
1     v  
1      w  
1      x  

这里会有许多ID类似的ID。我只显示了一个ID(1)。另外在这里我使用的字母实际上是200-500个字符长的字符串。我尝试了几乎没有修改的SQL方法,如果一个ID有100行或者以下的行,它可以正常工作,但是对于更多行(即使将长字符串转换为数字后)也会产生字符串连接错误。任何人都可以建议一个强大的基于过程的方法相同。我尝试了一些,但它不适用于超过300行的ID。

1 个答案:

答案 0 :(得分:0)

with 
  t_n as (
    select prev, curr, next, rownum n from your_table
  ),
  t_br as (
    select prev, curr, 
      '<'||listagg(n, '|<')within group(order by n)||'|' br,
      count(0) cnt
    from t_n
    group by prev, curr
  ),
  t_mp as (
    select 
      '|'||listagg(list)within group(order by null) list
    from (
      select replace(br, '<') list
      from t_br
      where cnt > 1
    )
  ),
  t_path(step, curr, next, used) as (
    select 1, curr, next, ''
    from t_n where prev is null
    union all
    select step + 1, t_br.curr, t_n.next, 
      case when instr(list, '|'||n||'|') = 0 then used
      else used||n||'|' end
    from t_mp, t_path
    join t_br 
      on next = t_br.curr and t_path.curr = prev
    join t_n 
      on n = regexp_substr(br,'^(<('||used||'0)\|)*(<(\d+))?',1,1,'',4)
  ) cycle step set is_cycle to 'Y' default 'N'
select step, curr from t_path order by 1;

fiddle