【问题标题】:Wrong order of elements after GROUP BY using ST_MakeLine使用 ST_MakeLine 进行 GROUP BY 后的元素顺序错误
【发布时间】:2021-04-01 09:57:51
【问题描述】:

我有一个表(嗯,它是 CTE),其中包含路径、节点 ID 数组和具有几何形状的节点表。我正在尝试使用它们的开始和结束节点以及几何图形来选择路径,如下所示:

SELECT *
FROM (
    SELECT t.path_id, t.segment_num, t.start_node, t.end_node, ST_MakeLine(n.geom) AS geom
      FROM (SELECT path_id, segment_num, nodes[1] AS start_node, nodes[array_upper(nodes,1)] AS end_node, unnest(nodes) AS node_id
        FROM paths            
      ) t
      JOIN nodes n ON n.id = t.node_id
      GROUP BY path_id, segment_num, start_node, end_node
    ) rs

当我在单个路径样本上尝试它时,这似乎工作得很好,但是当我在大型数据集上运行它时,少量生成的几何图形很糟糕 - 显然ST_MakeLine 接收到的点顺序错误。我怀疑并行聚合会导致错误的顺序,但也许我在这里遗漏了其他东西?

如何确保ST_MakeLine 中的点顺序正确?

如果我对并行聚合的看法是正确的,postgres docs 说的是 Scans of common table expressions (CTEs) are always parallel restricted,但这是否意味着我必须使用未嵌套的数组制作 CTE 并将其标记为 AS MATERIALIZED,因此它不会被优化回查询?

【问题讨论】:

  • ST_MakeLine(n.geom ORDER BY ...) 你需要t 中的排序列。 segment_num 大概?

标签: sql postgresql postgis


【解决方案1】:

谢谢你提醒我ST_MakeLine(geom ORDER BY something) 的可能性,ST_MakeLine 毕竟是聚合函数。我没有任何可用的显式排序列(顺序是nodes 数组中的位置,但一个节点可以出现多次)。幸运的是,unnest 可以在带有WITH ORDINALITY 的 FROM 子句中使用,因此为我创建了一个排序列。工作解决方案:

SELECT *
FROM (SELECT t.path_id, t.segment_num, t.start_node, t.end_node, ST_MakeLine(n.geom ORDER BY node_order) AS geom
      FROM (SELECT path_id, segment_num, nodes[1] AS start_node, nodes[array_upper(nodes,1)] AS end_node, a.elem AS node_id, a.nr AS node_order
        FROM paths, unnest(nodes) WITH ORDINALITY a(elem, nr)
      ) t
      JOIN nodes n ON n.id = t.node_id
      GROUP BY path_id, segment_num, start_node, end_node
    ) rs

【讨论】:

    【解决方案2】:

    为了让ST_MakeLine 以正确的顺序创建 LineString,您必须使用 ORDER BY 明确声明它。以下示例显示了点的顺序如何对输出产生巨大影响:

    无需订购

    WITH j (id,geom) AS (
     VALUES 
      (3,'SRID=4326;POINT(1 2)'::geometry),
      (1,'SRID=4326;POINT(3 4)'::geometry),
      (0,'SRID=4326;POINT(1 9)'::geometry),
      (2,'SRID=4326;POINT(8 3)'::geometry)
    )
    SELECT ST_MakeLine(geom) FROM j;
    

    通过id订购:

    WITH j (id,geom) AS (
     VALUES 
      (3,'SRID=4326;POINT(1 2)'::geometry),
      (1,'SRID=4326;POINT(3 4)'::geometry),
      (0,'SRID=4326;POINT(1 9)'::geometry),
      (2,'SRID=4326;POINT(8 3)'::geometry)
    )
    SELECT ST_MakeLine(geom ORDER BY id) FROM j;
    

    演示:db<>fiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 1970-01-01
      • 2011-07-22
      • 1970-01-01
      • 2016-06-25
      • 1970-01-01
      • 2022-11-22
      相关资源
      最近更新 更多