保持第一个父亲递归(开始... IN ...,连接依据)

时间:2015-01-23 09:45:33

标签: sql oracle recursion

我正在尝试跟踪在递归查询中使用'in'运算符给出的第一个父级。

这是(高度简化的;-))问题:

我有一个表(myTable),带有仪器互连,如:

    SENSOR_IN | SENSOR_OUT
    ----------------------
            5 |         6
            3 |         5
            7 |         8
            2 |         7
            1 |         2

从这个表中我想得到一个带有“level”的连接链,如:

    SELECT level, sensor_in, sensor_out
    FROM myTable
    START WITH sensor_out IN (6,8)
    CONNECT BY sensor_out = prior sensor_in
;

这会让我感觉像[没有测试,只是为了解释]:

    LEVEL | SENSOR_IN | SENSOR_OUT
    --------------------------------
         1          5 |         6
         1          7 |         8
         2          3 |         5
         2          2 |         7

我想知道添加一个名为的列,让我们说“第一个”,给出作为主要父级的每一行(所以“IN()”语句中给出的值,而不是表中的“true”主要父级=>在我的特定情况下,实际上总是相同的)。因此像:

    LEVEL | SENSOR_IN | SENSOR_OUT | FIRST
    ---------------------------------------
         1          5 |         6  |      6
         1          7 |         8  |      8
         2          3 |         5  |      6
         2          2 |         7  |      8

任何(简单)想法?

备注:如果某人有一个简单的方法可以为每个“第一个”在一行中返回整个链,那就更好了!类似的东西:

    FIRST | CHAIN
    -------------
        8 | 8;7;2
        6 | 6;5;3

提前致谢!

3 个答案:

答案 0 :(得分:1)

您可以使用the connect_by_root operator获取根节点:

SELECT level, sensor_in, sensor_out, connect_by_root sensor_out as first

您的示例输出无论如何都不匹配您的数据,但只需将其添加到您的查询中即可:

     LEVEL  SENSOR_IN SENSOR_OUT      FIRST
---------- ---------- ---------- ----------
         1          5          6          6 
         2          3          5          6 
         1          7          8          8 
         2          2          7          8 
         3          1          2          8 

SQL Fiddle

对于问题的第二部分,您可以将其用作内联视图,并使用listagg()将值合并为一个:

SELECT first, listagg(sensor_out, ';') within group (order by lvl) as chain
FROM (
  SELECT level as lvl, sensor_in, sensor_out, connect_by_root sensor_out as first
  FROM myTable
  START WITH sensor_out IN (6,8)
  CONNECT BY sensor_out = prior sensor_in
)
GROUP BY first
ORDER BY first
;

     FIRST CHAIN               
---------- --------------------
         6 6;5                  
         8 8;7;2                

SQL Fiddle

或使用more pseudocolumns

SELECT first, chain
FROM (
  SELECT connect_by_root sensor_out as first,
    ltrim(sys_connect_by_path (sensor_out, ';'), ';') as chain,
    connect_by_isleaf as is_leaf
  FROM myTable
  START WITH sensor_out IN (6,8)
  CONNECT BY sensor_out = prior sensor_in
)
WHERE is_leaf = 1
;

     FIRST CHAIN               
---------- --------------------
         6 6;5                  
         8 8;7;2                

Yet another SQL Fiddle

答案 1 :(得分:1)

以下是一些您应该可以使用的功能,以获得您所追求的结果:

with mytable as (select 5 sensor_in, 6 sensor_out from dual union all
                 select 3 sensor_in, 5 sensor_out from dual union all
                 select 7 sensor_in, 8 sensor_out from dual union all
                 select 2 sensor_in, 7 sensor_out from dual union all
                 select 1 sensor_in, 2 sensor_out from dual)
SELECT level,
       sensor_in,
       sensor_out,
       connect_by_root sensor_out top,
       sys_connect_by_path (sensor_in, ';') path
FROM   myTable
START WITH sensor_out IN (6,8)
CONNECT BY sensor_out = prior sensor_in;

     LEVEL  SENSOR_IN SENSOR_OUT        TOP PATH      
---------- ---------- ---------- ---------- ----------
         1          5          6          6 ;5        
         2          3          5          6 ;5;3      
         1          7          8          8 ;7        
         2          2          7          8 ;7;2      
         3          1          2          8 ;7;2;1    

答案 2 :(得分:1)

它可能有点冗长,但我发现递归SQL更直观,更灵活,更符合标准(因此,值得掌握,因为有一天你实际上可能使用的是非Oracle产品)而不是connect-by。证明可以轻松计算各种输出:

with mytable as (select 5 sensor_in, 6 sensor_out from dual union all
                 select 3 sensor_in, 5 sensor_out from dual union all
                 select 7 sensor_in, 8 sensor_out from dual union all
                 select 2 sensor_in, 7 sensor_out from dual union all
                 select 1 sensor_in, 2 sensor_out from dual union all
                 select 9 sensor_in, 10 sensor_out from dual union all
                 select 10 sensor_in, 9 sensor_out from dual 
                ),
recursive_cte (first_sensor, last_sensor, forward_chain, backward_chain, chain_length, next_sensor)
as (
    -- recursion base rows
    select  sensor_out                         first_sensor,
            sensor_in                          last_sensor,
            cast(sensor_out as varchar2(4000)) forward_chain,  
            cast(sensor_out as varchar2(4000)) backward_chain,  
            1                                  chain_length,
            sensor_in                          next_sensor
    from mytable  
    where sensor_out in (6,8,10)
    union all
    -- recursion inductive rows
    select  first_sensor                           first_sensor,
            next_sensor                            last_sensor,
            forward_chain || ';' || next_sensor    forward_chain,
            next_sensor  || ';' || backward_chain  backward_chain,
            chain_length + 1                       chain_length,
            sensor_in                              next_sensor
    from    recursive_cte 
            left join mytable on sensor_out = next_sensor
    where   next_sensor is not null
    )
    cycle next_sensor set is_infinite_loop to 'Y' default 'N'
select  first_sensor, last_sensor, forward_chain, backward_chain, chain_length, 
        is_infinite_loop 
from    recursive_cte
where   next_sensor is null
        or is_infinite_loop = 'Y'
order by first_sensor;

╔══════════════╦═════════════╦═══════════════╦════════════════╦══════════════╦══════════════════╗
║ FIRST_SENSOR ║ LAST_SENSOR ║ FORWARD_CHAIN ║ BACKWARD_CHAIN ║ CHAIN_LENGTH ║ IS_INFINITE_LOOP ║
╠══════════════╬═════════════╬═══════════════╬════════════════╬══════════════╬══════════════════╣
║            6 ║           3 ║ 6;5;3         ║ 3;5;6          ║            3 ║ N                ║
║            8 ║           1 ║ 8;7;2;1       ║ 1;2;7;8        ║            4 ║ N                ║
║           10 ║          10 ║ 10;9;10       ║ 10;9;10        ║            3 ║ Y                ║
╚══════════════╩═════════════╩═══════════════╩════════════════╩══════════════╩══════════════════╝