繁体   English   中英

查找彼此相距 x 个单位内的所有记录

[英]Find all records within x units of each other

我有一张这样的表:

CREATE TABLE t(idx integer primary key, value integer);
INSERT INTO t(idx, value)
VALUES
    (1, 1),
    (2, 2),
    (3, 3),
    (4, 6),
    (5, 7),
    (6, 12)

我想返回值彼此相差在 2 以内的所有记录组,并使用关联的组标签作为新列来识别它们。

我想也许递归查询可能是合适的......但我的 sql-fu 是缺乏的。

我假设您想对行进行分组,以便每个组中的任何两个值最多只能相差 2。那么您是对的,递归查询就是解决方案。 在每一级递归中,新组的边界都被预先计算。 组是不相交的,所以最后用计算出的组号加入原始表,然后按这个号分组。 数据库小提琴在这里。

with recursive r (minv,maxv,level) as (
  select min(t.value), min(t.value) + 2, 1
  from t
  union all
  select minv, maxv, level from (
    select t.value as minv, t.value + 2 as maxv, r.level + 1 as level, row_number() over (order by minv) rn
    from r
    join t on t.value > r.maxv
  ) x where x.rn = 1
)
select r.level
     , format('ids from %s to %s', min(t.idx), max(t.idx)) as id_label
     , format('values from %s to %s', min(t.value), max(t.value)) as value_label
from t join r on t.value between r.minv and r.maxv
group by r.level
order by r.level

(递归部分的内部查询只是将新添加的行数限制为一个。更简单的子句select min(t.value), min(t.value) + 2是不可能的,因为递归中不允许聚合函数部分,分析函数是解决方法。)

您可以使用递归 CTE:

with recursive tt as (
      select t.*, row_number() over (order by idx) as seqnum
      from t
     ),
     cte as (
      select idx, value, value as grp,
             seqnum, 1 as lev
      from tt
      where seqnum = 1
      union all
      select tt.idx, tt.value, 
             (case when tt.value > grp + 2 then tt.value else cte.grp end),
             tt.seqnum, 1 + lev
      from cte join
           tt
           on tt.seqnum = cte.seqnum + 1
     )
select *
from cte;

是一个 db<>fiddle。 请注意,这添加了一个值为“4”的行,以显示前四行分为两组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM