计算一系列线段的长度

Calculate length of a series of line segments

我有一个 table 如下所示:

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是一些点的三维space坐标,一条曲线从第一行到最后一行穿过所有对应的点。我需要计算 "node" 列不为空的两个相邻点之间的曲线长度。

如果我可以将结果直接插入到另一个具有三列的 table 中,那就太好了:"first_node"、"second_node"、"curve_length".

我不需要在曲线中插入额外的点,只需要累加所有直线的长度,例如,为了计算节点 100 和 200 之间的曲线长度,我需要对长度求和3 条直线:(1,2,3)<->(2,2,3), (2,2,3)<->(2,2,4), (2,2,4)<- >(2,2,5)

编辑 table有一个ID列,从第一行到最后一行递增。

要获取 SQL 中的先前值,请使用 lag window 函数,例如

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

这让您可以在 3-D space 中获取给定线段的上一个点和下一个点。从那里您可以使用 regular geometric maths.

简单地计算线段长度

You'll now have the lengths of each segment(sqlfiddle 查询)。您可以使用此作为其他查询的输入,使用 SELECT ... FROM (SELECT ...) 子查询或 CTE (WITH ....) 术语。

事实证明,从节点段长度到节点到节点长度是很尴尬的。您需要使用递归 CTE 或 a window function.

创建一个跨越空条目的 table

我遇到了这个怪物:

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)

要直接插入另一个 table,请使用 INSERT INTO ... SELECT ...