postgresql 计算一系列线段的长度

mpgws1up  于 2022-11-23  发布在  PostgreSQL
关注(0)|答案(1)|浏览(479)

我有一个如下的表:

X | Y | Z | node
----------------
1 | 2 | 3 | 100
2 | 2 | 3 | 
2 | 2 | 4 | 
2 | 2 | 5 | 200
3 | 2 | 5 | 
4 | 2 | 5 | 
5 | 2 | 5 | 300

X、Y、Z是一些点的三维空间坐标,一条曲线从第一行到最后一行经过所有对应的点,我需要计算“节点”列不为空的两个相邻点之间的曲线长度。
如果我能直接将结果插入到另一个有三列的表中,那就太好了:“第一节点”、“第二节点”、“曲线长度”。
我不需要在曲线中插入额外的点,只需要累加所有直线的长度,例如,为了计算节点100和200之间的曲线长度,我需要对3条直线的长度求和:(1、2、3)<->(2、2、3)(2、<->2、4)(2、2、4)<->(2、2、5)

EDIT该表有一个ID列,该列从第一行到最后一行按升序排列。

8i9zcol2

8i9zcol21#

要在SQL中获取以前的值,请使用lag窗口函数,例如

SELECT 
  x, 
  lag(x) OVER (ORDER BY id) as prev_x, ...
FROM ...
ORDER BY id;

这样你就可以得到给定线段在三维空间中的上一个和下一个点,然后你就可以用regular geometric maths简单地计算线段的长度。
现在,您将获得每个段的长度(sqlfiddle查询),您可以使用SELECT ... FROM (SELECT ...)子查询或CTE(WITH ....)项将其作为其他查询的输入。
从节点段长度到节点到节点的长度是非常困难的,需要使用递归CTE或a window function创建一个包含空条目的表。
我最后得到了这个怪物:

SELECT
  array_agg(from_id) AS seg_ids,
  -- 'max' is used here like 'coalese' for an aggregate,
  -- since non-null is greater than null
  max(from_node) AS from_node,
  max(to_node) AS to_node,
  sum(seg_length) AS seg_length
FROM (
  -- lengths of all sub-segments with the null last segment
  -- removed and a partition counter added
  SELECT
   *,
   -- A running counter that increments when the
   -- node ID changes. Allows us to group by series
   -- of nodes in the outer query.
   sum(CASE WHEN from_node IS NULL THEN 0 ELSE 1 END) OVER (ORDER BY from_id) AS partition_id
  FROM
  (
    -- lengths of all sub-segments
    SELECT
      id AS from_id,
      lead(id, 1) OVER (ORDER BY id) AS to_id,
      -- length of sub-segment
      sqrt(
        (x - lead(x, 1) OVER (ORDER BY id)) ^ 2 +
        (y - lead(y, 1) OVER (ORDER BY id)) ^ 2 +
        (z - lead(z, 1) OVER (ORDER BY id)) ^ 2
      ) AS seg_length,
      node AS from_node,
      lead(node, 1) OVER (ORDER BY id) AS to_node
    FROM
      Table1
  ) sub
  -- filter out the last row
  WHERE to_id IS NOT NULL
) seglengths
-- Group into series of sub-segments between two nodes
GROUP BY partition_id;

分区技巧归功于How do I efficiently select the previous non-null value?
结果:

seg_ids | to_node | from_node | seg_length 
---------+---------+---------+------------
 {1,2,3} |     100 |     200 |          3
 {4,5,6} |     200 |     300 |          3
(2 rows)

若要直接插入另一个数据表,请使用INSERT INTO ... SELECT ...

相关问题