【问题标题】:PostgreSQL 11.2 Group objects by common attribute in single tablePostgreSQL 11.2 按单个表中的公共属性对对象进行分组
【发布时间】:2023-11-28 13:44:01
【问题描述】:

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

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

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

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,因为它永远不会匹配起点/终点相关性):

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 运行查询):

有人知道一种方法来做我想做的事吗?

【问题讨论】:

    标签: sql postgresql group-by graph-algorithm recursive-query


    【解决方案1】:

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

    这个想法是首先确定每种颜色的所有起点。为此,您可以使用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

    颜色 |小路 :---- | :-------- 蓝色 | {H} 蓝色 | {我,J} 绿色 | {A B C D} 红色 | {E,F,G}

    【讨论】:

    • 谢谢!我猜它会是这样的,但我没能把头绕过去。效果很好。