Search code examples
sqloracle-databaserecursive-queryself-join

Recursive query - Oracle


I have this data and expected result:

    Data                Expected Result 
No  A   B                  A    B
1   10  500               10    500                          
2   10  c=20              20    400
3   20  400               30    600
4   30  600               30    700
5   30  c=40              30    800
6   30  c=50              40    700
7   40  700               50    900
8   50  c=60              60    900
9   60  c=70              70    900
10  70  900               10    400

I need to perform a self join and get the result.

  • For line number 1 the expected result is same as the row.
  • For line number 2, I need to take the substring of column B (c=20) as 20 and join with column B and get the result as 400.
  • Lines number 5 and 6 need to substring column B and get the result from column A.

I tried a recursive query, but still am not getting the expected result.

with rec(A, B, nested) as
(
select A, B, case when instr(B, 'C=') != 0 then substr(B, instr(B, 'C=')) as nested
from table
union all
select A, rec.B from table
inner join rec
on (table.A = rec.nested)
)
select A, B, nested from rec;

Solution

  • Answer for the initial version of the question

    You do not need a recursive query. To get your desired output you just need to exclude the rows where B starts with c=:

    SELECT a, b
    FROM   table_name
    WHERE b NOT LIKE 'c=%';
    

    Which, for the sample data:

    CREATE TABLE table_name (no, a, b) AS
      SELECT 1, 10, '500'  FROM DUAL UNION ALL
      SELECT 2, 10, 'c=20' FROM DUAL UNION ALL
      SELECT 3, 20, '400'  FROM DUAL UNION ALL
      SELECT 4, 30, '600'  FROM DUAL UNION ALL
      SELECT 5, 30, 'c=40' FROM DUAL UNION ALL
      SELECT 6, 30, 'c=50' FROM DUAL UNION ALL
      SELECT 7, 40, '700'  FROM DUAL UNION ALL
      SELECT 8, 50, '800'  FROM DUAL;
    

    Outputs your desired output:

    A B
    10 500
    20 400
    30 600
    40 700
    50 800

    fiddle

    Answer for the 3rd edit of the question

    You can use a hierarchical query:

    SELECT DISTINCT
           CONNECT_BY_ROOT a AS a,
           b
    FROM   table_name
    WHERE  CONNECT_BY_ISLEAF = 1
    CONNECT BY
           PRIOR b LIKE 'c=%'
    AND    PRIOR SUBSTR(b, 3) = a
    ORDER BY a, b;
    

    Which, for the sample data:

    CREATE TABLE table_name (no, a, b) AS
      SELECT  1, 10, '500'  FROM DUAL UNION ALL
      SELECT  2, 10, 'c=20' FROM DUAL UNION ALL
      SELECT  3, 20, '400'  FROM DUAL UNION ALL
      SELECT  4, 30, '600'  FROM DUAL UNION ALL
      SELECT  5, 30, 'c=40' FROM DUAL UNION ALL
      SELECT  6, 30, 'c=50' FROM DUAL UNION ALL
      SELECT  7, 40, '700'  FROM DUAL UNION ALL
      SELECT  8, 50, 'c=60' FROM DUAL UNION ALL
      SELECT  9, 60, 'c=70' FROM DUAL UNION ALL
      SELECT 10, 70, '900'  FROM DUAL;
    

    Outputs:

    A B
    10 400
    10 500
    20 400
    30 600
    30 700
    30 900
    40 700
    50 900
    60 900
    70 900

    fiddle