PostgreSQL 11.2 在单个表中按共同属性对对象进行分组。

问题描述 投票:2回答:1

path :----enter image description here

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

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

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被返回(如果我使用一个 WHERE 句子,同 ON 条件下,H不会被返回,因为它永远不会匹配startpointendpoint的相关性)。

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

有了这个结果。

enter image description here

从这里我不知道该怎么做。我在PostGIS中使用了一个聚合函数来合并行,所以我想我需要有这样的结果(这样我就可以运行一个查询,用 GROUP BY) :

enter image description here

有谁知道有什么方法可以做我想做的事情?

sql postgresql group-by graph-algorithm recursive-query
1个回答
2
投票

假设你的数据没有圆圈,如你的样本数据所示,一个选择是使用递归查询。

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

DB Fiddle上的演示:

颜色 
© www.soinside.com 2019 - 2024. All rights reserved.