计算一系列线段的长度
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 ...
。
我有一个 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 ...
。