Postgres递归-获取每个孩子的root ID(并忽略错误的数据)

时间:2018-12-06 07:09:08

标签: postgresql

我正在努力调整此处已针对此特定用例发布的递归解决方案。

我有一张桌子,如下表

 +-------------+------------+-------------+------------+
 | previous_id | current_id | external_id | day        |
 +-------------+------------+-------------+------------+
 | A1          | U1         | A1          | 2018-10-10 |
 +-------------+------------+-------------+------------+
 | U1          | U1         | A2          | 2018-10-11 |
 +-------------+------------+-------------+------------+
 | U1          | U2         | A2          | 2018-10-12 |
 +-------------+------------+-------------+------------+
 | U2          | U3         | A3          | 2018-10-13 |
 +-------------+------------+-------------+------------+
 | U3          | U3         | A4          | 2018-10-14 |
 +-------------+------------+-------------+------------+
 | U2          | U5         | A5          | 2018-10-15 |
 +-------------+------------+-------------+------------+

其中有几个特质,我(很不幸)无法解决,因为它是由外部系统定期生成的。具体来说,这些是:

  • 可以通过previous_id = external_id标识根;所需的root_id是该行中的current_id
  • previous_id可能等于current_id
  • 不同的current_id可能具有相同的parent_id

我要从中生成的输出如下:

 +---------+------------+-------------+------------+
 | root_id | current_id | external_id | day        |
 +---------+------------+-------------+------------+
 | U1      | U1         | A1          | 2018-10-10 |
 +---------+------------+-------------+------------+
 | U1      | U1         | A2          | 2018-10-11 |
 +---------+------------+-------------+------------+
 | U1      | U2         | A2          | 2018-10-12 |
 +---------+------------+-------------+------------+
 | U1      | U3         | A3          | 2018-10-13 |
 +---------+------------+-------------+------------+
 | U1      | U3         | A4          | 2018-10-14 |
 +---------+------------+-------------+------------+
 | U1      | U5         | A5          | 2018-10-15 |
 +---------+------------+-------------+------------+ 

这可能吗?我在这里用示例数据设置了一个SQL提琴:http://sqlfiddle.com/#!15/58efb/6

1 个答案:

答案 0 :(得分:1)

demo: db<>fiddle

WITH RECURSIVE tree AS (
    -- 1
    SELECT 
        current_id as root_id,
        previous_id,
        current_id,
        external_id,
        day
    FROM list 
    WHERE previous_id = external_id

    UNION

    -- 2
    SELECT
        t.root_id,
        l.previous_id,
        l.current_id,
        l.external_id,
        l.day
    FROM
        list l
    JOIN
        tree t
    -- 3
    ON t.current_id = l.previous_id AND t.previous_id <> t.current_id

)
SELECT 
    root_id,
    current_id,
    external_id,
    day 
FROM tree
ORDER BY day

这是直接递归问题:

  1. 递归的起点查询:获取所有根元素。
  2. 递归部分
  3. 针对该表联接最后一个递归结果:最后一个结果给出当前查询的父级。因此,将最后一个current_id作为新的previous_id加入。为了避免像第二行那样发生无限循环,请勿将previous_idcurrent_id具有相同值的行连接起来。