开发者

Directed acyclic graph: find all paths from a specific node

How do I find all the paths from a specific node (node 36 in the example)?

Let's say we have two tables:

CATEGORIES      CATEG_PARENTS
id              idc  | idparent
--              ----------------
1               2      1
2               2      20
5               5      2
8               8      5
20              20     1
22              22     8
30              22     20
31              30     20
36              31     22
                31     30
      开发者_开发百科          36     31

These are two possible representations:

Directed acyclic graph: find all paths from a specific node

(source: glopes at nebm.ist.utl.pt)

Directed acyclic graph: find all paths from a specific node

(source: glopes at nebm.ist.utl.pt)

This is the output I desire:

ids
-------------------
"{1,20,22,31}"
"{1,20,2,5,8,22,31}"
"{1,20,30,31}"
"{1,2,5,8,22,31}"

One path per line, written as an integer array.

(I'm going to write the answer I came up with, but I'll accept any that's simpler, if any)


WITH RECURSIVE parent_line(path, id) AS (
    SELECT ARRAY[(row_number() OVER (PARTITION BY CP.idc))::integer], C.id
    FROM categorias C JOIN categ_parents CP ON C.id = CP.idparent
    WHERE CP.idc = 36
    UNION ALL
    SELECT PL.path || (row_number() OVER (PARTITION BY PL.id))::integer,
        C.id
    FROM categorias C
    JOIN categ_parents CP ON C.id = CP.idparent
    JOIN parent_line PL on CP.idc = PL.id
),
real_parent_line(path, chainid, id) AS (
    SELECT PL.path, (row_number() OVER (PARTITION BY PL.id)),
        PL.id
    FROM parent_line PL
    WHERE PL.id IN (
        SELECT id
        FROM categorias C LEFT JOIN categ_parents CP
            ON (C.id = CP.idc)
        WHERE CP.idc IS NULL
    )
    UNION ALL
    SELECT PL.path, chainid, PL.id
    FROM parent_line PL, real_parent_line RPL
    WHERE array_upper(PL.path,1) + 1 = array_upper(RPL.path,1)
        AND PL.path = RPL.path[1:(array_upper(RPL.path,1)-1)]
)
SELECT array_accum(id) AS ids
FROM real_parent_line RPL
GROUP BY chainid;

The first WITH clause gives this:

path             | id
------------------------
"{1}"              31
"{1,1}"            22
"{1,2}"            30
"{1,1,1}"          20
"{1,1,2}"          8
"{1,2,1}"          20
"{1,1,2,1}"        5
"{1,1,1,1}"        1
"{1,2,1,2}"        1
"{1,1,2,1,1}"      2
"{1,1,2,1,1,1}"    1
"{1,1,2,1,1,2}"    20
"{1,1,2,1,1,2,1}"  1

Thanks to #postgresql@freenode for some help.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜