我正在尝试找出一种方法来推理一个相当简单的问题:
表
有两列,foo
具有唯一标识符,id
具有随机value
值。给定一个常数numeric
值,找到threshold
在该阈值内的值组 - 该组的计数以及该组的平均值。value
让我们做好准备 - 这是
foo
表(在 PostgreSQL 中):
CREATE TABLE foo (
id serial PRIMARY KEY,
value numeric NOT NULL
);
查询应该返回计数和平均值 - 条目不能被重复计算,所以我希望根据某个阈值“接近度”值将行“分成组” - 你可以想到
threshold
半径值:
SELECT AVG(foo.value), COUNT(foo.id)
FROM foo
GROUP BY foo.value; -- where I'm stuck
上面的查询只会将聚合函数应用于其中
foo.value
有 exact 重复项的行 - 我想要的是类似于以下伪 SQL 的东西:
SELECT AVG(foo.value), COUNT(foo.id)
FROM foo
GROUP BY
(row_being_grouped.value <= foo.value + threshold)
AND
(row_being_grouped.value >= foo.value - threshold);
我不确定这是否有意义。我想知道我是否可以在不执行子查询的情况下解决这个问题 - 也许在找到存储桶内的平均值之前以某种方式“存储”行?
如果“阈值”可以理解为同一组值之间允许的最大间隙,那么这是一个明确定义的间隙和岛屿问题。
解决办法如下:
SELECT grp, count(*) AS grp_count, round(avg(value), 2) AS grp_avg
FROM (
SELECT count(gap) OVER (ORDER BY value) AS grp, *
FROM (
SELECT value
, value - lag(value) OVER (ORDER BY value) > 150 OR null AS gap
FROM foo
) sub1
) sub2
GROUP BY grp
ORDER BY grp;
小提琴(带有分步演示)
解释和更多链接:
为了方便和简短的代码,我使用布尔逻辑(
true OR null
→true
,false OR null
→null
),并且count()
忽略null
值。参见:
如果这更多的是栅格/集群/粒化/网格问题,您需要准确定义要选择哪些行作为“焦点”,或者独立栅格/网格的确切性质。
我认为阈值为 123.45...然后:
WITH
threshold AS
(SELECT 123.45 AS threshold_value, MIN(value) AS MI, MAX(value) AS MA
FROM foo),
slices AS
(SELECT threshold_value, GENERATE_SERIES AS boundary
FROM threshold
LATERAL GENERATE_SERIES(MI - threshold_value / 2.0, MA + threshold_value / 2.0, threshold_value) AS boundaries),
places AS
(SELECT Id, val, boundary, threshold_value
FROM foo
JOIN slices ON val >= boundary AND val < boundary + threshold_value)
SELECT COUNT(val) AS COUNT_VAL, boundary AS BOUND_LOW, boundary + threshold_value AS BOUND_HIGH
FROM places
GROUP BY boundary, boundary + threshold_value
ORDER BY BOUND_LOW;
未测试...