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
color | path
:---- | :--------
blue | {H}
blue | {I,J}
green | {A,B,C,D}
red | {E,F,G}
我在网络数据集上工作(使用 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
color | path :---- | :-------- blue | {H} blue | {I,J} green | {A,B,C,D} red | {E,F,G}