我想通过插值两个最近邻居之间的值来计算一个值。 我有一个子查询,返回邻居的值及其相对距离,以两列两个元素的形式。
让我们说:
(select ... as value, ... as distance
from [get some neighbours by distance] limit 2) as sub
如何通过线性插值计算点的值?是否可以在单个查询中执行此操作?
示例:我的点在距离1处有一个值为10的邻居A,在距离4处有一个值为20的邻居B.该函数应为我的点返回值10 * 4 + 20 * 1 / 5 = 12
我尝试了明显的方法
select sum(value * (sum(distance)-distance)) / sum(distance)
将失败,因为您无法使用group子句中的group子句。使用另一个子查询返回总和也是不可能的,因为那样我就无法同时转发各个值。
答案 0 :(得分:1)
如果你真的想要介于point
之间,那么有一种内置的方式(但不是聚合函数):
SELECT center(box(x.mypoint,y.mypoint))
FROM ([get some neighbours by distance] order by value limit 1) x
,([get some neighbours by distance] order by value offset 1 limit 1) y;
如果你想要平均距离:
SELECT avg(x.distance)
FROM ([get some neighbours by distance] order by value limit 2) as x
请参阅手册中的geometrical function和aggregate functions。
对于添加的示例,查询可能如下所示:
SELECT (x.value * 4 + y.value) / 5 AS result
FROM ([get some neighbours by distance] order by value limit 1) x
,([get some neighbours by distance] order by value offset 1 limit 1) y;
我添加了遗失()
以获得您期望的结果!
或者,我最后一次刺伤它:
SELECT y.x, y.x[1], (y.x[1] * 4 + y.x[2]) / 5 AS result
FROM (
SELECT ARRAY(
SELECT value FROM tbl WHERE [some condition] ORDER BY value LIMIT 2
) x
) y
如果您提供了完整的查询和表格定义,那么会更容易。
答案 1 :(得分:1)
这是一个丑陋的黑客(基于滥用的CTE;)。它的症结在于
value1 * distance2 + value2 * distance1
可以通过除以distance1 * distance2,重写为
value1/distance1 + value2/distance2
因此,产品(或部门)可以留在他们的行内。在求和之后,乘以(distance1 * distance2)将结果重新调整为所需的输出。对两个以上邻居的推广留给读者练习.YMMV
DROP TABLE tmp.points;
CREATE TABLE tmp.points
( pname VARCHAR NOT NULL PRIMARY KEY
, distance INTEGER NOT NULL
, value INTEGER
);
INSERT INTO tmp.points(pname, distance, value) VALUES
( 'A' , 1, 10 )
, ( 'B' , 4, 20 )
, ( 'C' , 10 , 1)
, ( 'D' , 11 , 2)
;
WITH RECURSIVE twin AS (
select 1::INTEGER AS zrank
, p0.pname AS zname
, p0.distance AS dist
, p0.value AS val
, p0.distance* p0.value AS prod
, p0.value::float / p0.distance AS frac
FROM tmp.points p0
WHERE NOT EXISTS ( SELECT * FROM tmp.points px
WHERE px.distance < p0.distance)
UNION
select 1+twin.zrank AS zrank
, p1.pname AS zname
, p1.distance AS dist
, p1.value AS val
, p1.distance* p1.value AS prod
, p1.value::float / p1.distance AS frac
FROM tmp.points p1, twin
WHERE p1.distance > twin.dist
AND NOT EXISTS ( SELECT * FROM tmp.points px
WHERE px.distance > twin.dist
AND px.distance < p1.distance
)
)
-- SELECT * from twin ;
SELECT min(zname) AS name1, max(zname) AS name2
, MIN(dist) * max(dist) *SUM(frac) / SUM(dist) AS score
FROM twin
WHERE zrank <=2
;
结果:
CREATE TABLE
INSERT 0 4
name1 | name2 | score
-------+-------+-------
A | B | 12
更新:这个有点更干净...仍未处理关系(需要在外部查询中使用窗口函数或LIMIT 1子句)
WITH RECURSIVE twin AS (
select 1::INTEGER AS zrank
, p0.pname AS name1
, p0.pname AS name2
, p0.distance AS dist
FROM tmp.points p0
WHERE NOT EXISTS ( SELECT * FROM tmp.points px
WHERE px.distance < p0.distance)
UNION
select 1+twin.zrank AS zrank
, twin.name1 AS name1
, p1.pname AS name2
, p1.distance AS dist
FROM tmp.points p1, twin
WHERE p1.distance > twin.dist
AND NOT EXISTS ( SELECT * FROM tmp.points px
WHERE px.distance > twin.dist
AND px.distance < p1.distance
)
)
SELECT twin.name1, twin.name2
, (p1.distance * p2.value + p2.distance * p1.value) / (p1.distance+p2.distance) AS score
FROM twin
JOIN tmp.points p1 ON (p1.pname = twin.name1)
JOIN tmp.points p2 ON (p2.pname = twin.name2)
WHERE twin.zrank =2
;