PostgreSQL 11.2 按单个 table 中的公共属性对对象进行分组

PostgreSQL 11.2 Group objects by common attribute in single table

我在网络数据集上工作(使用 PostGIS 扩展,但由于我目前使用 pgrouting 的方法是我发现的唯一一种可以做我想做的事情,而且 运行 非常痛苦,我想尝试一下并按属性处理)如下图:

每个部分(字母)是一个对象,颜色是对象的相关属性。

代表这个的table是这样定义的:

CREATE TABLE lines (gid text, color text, startpoint integer, endpoint integer);
INSERT INTO lines (gid, color, startpoint, endpoint) 
VALUES 
    ('A','green', 1, 2), 
    ('B','green', 2, 3), 
    ('C','green', 3, 4), 
    ('D','green', 4, 5), 
    ('E','red', 2, 6), 
    ('F','red', 6, 7), 
    ('G','red', 7, 8), 
    ('H','blue', 3, 9), 
    ('I','blue', 4, 10), 
    ('J','blue', 10, 11);

我想要得到的结果是所有相同颜色的对象相互接触而成的聚合对象。所以这里有 4 个对象:{A,B,C,D}、{E,F,G}、{H} 和 {I,J}。我假设要走的路是使用起点和终点值,因为它们决定了对象的接触方面。

现在我使用下面的代码,使用 JOIN 返回对象 H(如果我使用与 ON 条件相同的 WHERE 子句H 不会返回,因为它永远不会匹配 startpoint/endpoint 相关性):

SELECT a.gid, b.gid, a.color 
FROM lines a 
LEFT JOIN lines b ON a.gid > b.gid AND (a.startpoint = b.endpoint OR a.endpoint = b.startpoint) AND a.color = b.color

结果如下:

从这里我不知道该怎么做。我在 PostGIS 中使用聚合函数来合并线条,所以我想我需要以这种形式获得结果(这样我就可以 运行 使用 GROUP BY 进行查询):

有人知道做我想做的事情的方法吗?

假设您的数据没有圆圈,如示例数据所示,一个选项是使用递归查询。

想法是首先确定每种颜色的所有起点。为此,您可以使用 not exsits:

select l.*
from lines l
where not exists (
    select 1 from lines l1 where l1.endpoint = l.startpoint and l1.color = l.color
)

从那里开始,您可以递归地遍历结构,方法是查找从前一个结束处开始的相同颜色的线条,同时跟踪数组中的线条路径。

最后一步是过滤结果集以识别非重叠路径。为此,我们可以将 not exists 与包含运算符一起使用。

请注意,此技术允许分支(如果有的话)。

代码:

with recursive cte as (
    select l.*, array[gid] path
    from lines l
    where not exists (
        select 1 from lines l1 where l1.endpoint = l.startpoint and l1.color = l.color
    )
    union all 
    select l.*, c.path || l.gid
    from cte c
    inner join lines l on l.startpoint = c.endpoint and l.color = c.color
)
select color, path
from cte c
where not exists (select 1 from cte c1 where c1.path @> c.path and not c.path @> c1.path)
order by color, path

Demo on DB Fiddle:

color | path     
:---- | :--------
blue  | {H}      
blue  | {I,J}    
green | {A,B,C,D}
red   | {E,F,G}