How do I trace former ids using a recursive query?

时间:2015-10-30 22:55:14

标签: sql postgresql recursion postgresql-9.4

I have a table of provider information (providers) that contains the columns reporting_unit and predesessor. Predesessor is either null or contains the reporting_unit that that row used to represent. I need to find what the current reporting_unit for any provider is. By that I mean for any reporting_unit with a predesessor, that reporting_unit is the current_reporting_unit for the predesessor.

I am trying to use a recursive CTE to accomplish this because some of the time there are multiple links.

The table looks like this:

CREATE TABLE providers (
   reporting_unit TEXT,
   predesessor TEXT
);

INSERT INTO providers
VALUES
    (NULL, NULL),
    ('ARE88', NULL),
    ('99BX7', '99BX6'),
    ('99BX6', '99BX5'),
    ('99BX5', NULL)
;

The results I would like to get from that are:

reporting_unit | current_reporting_unit
---------------------------------------
       '99BX5' | '99BX7'
       '99BX6' | '99BX7'

My current query is :

WITH RECURSIVE current_ru AS (
    SELECT reporting_unit, predesessor
    FROM providers
    WHERE predesessor IS NULL

    UNION ALL

    SELECT P.reporting_unit, P.predesessor
    FROM providers P
         JOIN current_ru CR
         ON P.reporting_unit = CR.predesessor
    )
SELECT *
FROM current_ru
;

But that isn't giving me the results I'm looking for. I have tried a number of variations on this query but they all seem to end up in an infinite loop. How

1 个答案:

答案 0 :(得分:1)

You should find relations in the reverse order. Add depth column to find the deepest link:

with recursive current_ru (reporting_unit, predesessor, depth) as (
    select reporting_unit, predesessor, 1
    from providers
    where predesessor is not null
union
    select r.reporting_unit, p.predesessor, depth+ 1
    from providers p
    join current_ru r
    on p.reporting_unit = r.predesessor
    )
select *
from current_ru;

 reporting_unit | predesessor | depth 
----------------+-------------+-------
 99BX7          | 99BX6       |     1
 99BX6          | 99BX5       |     1
 99BX6          |             |     2
 99BX7          | 99BX5       |     2
 99BX7          |             |     3
(5 rows)

Now switch the two columns, change their names, eliminate null rows and select the deepest links:

with recursive current_ru (reporting_unit, predesessor, depth) as (
    select reporting_unit, predesessor, 1
    from providers
    where predesessor is not null
union
    select r.reporting_unit, p.predesessor, depth+ 1
    from providers p
    join current_ru r
    on p.reporting_unit = r.predesessor
    )
select distinct on(predesessor) 
    predesessor reporting_unit, 
    reporting_unit current_reporting_unit
from current_ru
where predesessor is not null
order by predesessor, depth desc;

 reporting_unit | current_reporting_unit 
----------------+------------------------
 99BX5          | 99BX7
 99BX6          | 99BX7
(2 rows)