【问题标题】:How do I conditionally merge rows based on rows difference in PostgreSQL?如何根据 PostgreSQL 中的行差异有条件地合并行?
【发布时间】:2017-06-10 13:53:18
【问题描述】:

我已经解决了一些相关的合并问题,但我的问题与现有问题略有不同。我的 PostgreSQL 9.5 数据库中有一个表,其中包含四列,即段(唯一组)、高度(数字)、下限(数字)和上限(数字)。样本数据如下:

Segment height  lower_limit upper_limit
A       19.3    112         142
A       19.3    142         172
A       20.3    172         202
A       20.3    202         232
A       19.3    232         262
A       19.3    262         292
B       22.1    203         233
B       22.1    233         263
B       22.1    263         293
B       22.1    293         323
B       22.1    323         353
B       22.1    353         383
C       18.9    136         166
C       18.9    166         196
C       18.9    196         226
C       27.1    286         316
C       27.1    316         346
C       6.5     346         376
C       6.5     376         406

我需要根据高度值的差异有条件地合并行。我会尝试分步解释:

  • 从第一个高度开始,检查前后行的差值是否小于等于1
  • 如果满足条件,则合并具有第一行下限和合并行上限的行
  • 如果所有行都合并到一个组中,则选择最常见的高度,第一行的下限和最后合并行的上限
  • 对其他组重复此操作

    基于上述,所需的输出可能如下:

    Segment height lower_limit upper_limit A 19.3 112 292 B 22.1 203 383 C 18.9 136 226 C 27.1 286 346 C 6.5 346 406

有人可以帮助我,以便我可以根据高度差值有条件地合并行吗?

【问题讨论】:

  • 有指定顺序的栏目吗?
  • 我在代码末尾添加了按 grp 行的顺序。根据所需的输出,我得到 5 行,但以这种方式:C、A、B、C、C。请查看所需的输出。
  • 通过segment,grp添加订单

标签: sql postgresql merge rows difference


【解决方案1】:

假设lower_limit列可用于排序,则可以使用

select segment,mode() within group(order by height),min(lower_limit),max(upper_limit) 
from (select t.*
      ,sum(case when abs(height-prev) <= 1 then 0 else 1 end) over(partition by segment order by lower_limit) as grp
      from (select t.*
            ,lag(height) over(partition by segment order by lower_limit) as prev
            from tbl t
           ) t
    ) t
group by segment,grp

【讨论】:

  • 我修改了最后两行。我添加了:按grp分组,按grp分段排序,分段。我根据需要得到了 A、B、C、C、C。除第一段 A 外,所有值都很好。对于 A;它给出了 20.3、112、292。知道为什么吗?
  • @JibranKhan ..检查编辑..使用mode 为您提供每个段最常见的高度和grp ..这是您所需要的。
  • 我有一个问题,如果我有一行或两行包含零,那么我怎样才能将那一行/行分成一个单独的组及其下限和上限。我认为需要在这一行中进行更改: sum(case when abs(height-prev)
【解决方案2】:
  -- setting reset points
  with  b as
  (
      select segment, height, lower_limit, upper_limit, 
             case when lag(height) over (partition by segment order by segment, height) is null
                       or abs(height - lag(height) over (partition by segment order by segment, height)) > 1
                  then 1 end as is_reset
      from   foo
  )
     -- setting groups
     , c as
     (
         select segment, height, lower_limit, upper_limit,
                sum(is_reset) over (order by segment, height) as grp
         from b
     )
       -- finding most common height
           select segment, mode() within group (order by height),
                  min(lower_limit) as lower_limit, 
                  max(upper_limit) as upper_limit
           from c
           group by segment, grp
段 |模式 |下限 |上限 :-------- | ----: | ----------: | ----------: 一个 | 19.30 | 112 | 292 乙| 22.10 | 203 | 383 C | 6.50 | 346 | 406 C | 18.90 | 136 | 226 C | 27.10 | 286 | 346

dbfiddle here

【讨论】:

  • 非常感谢。我在 dbfiddle 中测试了你的代码。它在那里工作得很好。完全相同的表在我的 Postgres 数据库中,但它返回了六行,即 A、B、C、C、C、C。一行 C(18.9、136、226)被返回了两次。知道为什么吗?
  • 执行第一个选择并确保它返回正确的顺序。
  • 好吧,我重新编写了整个代码,以确保我没有犯任何错误,但结果仍然相同。
  • 这个查询select segment, height, lower_limit, upper_limit, row_number() over (partition by segment order by (select 1)) id from foo是否返回正确的顺序?
  • 不,它没有。一行 C,18.9..... 位于结果的底部,如果您查看示例数据,这是不正确的。
猜你喜欢
  • 2021-12-10
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2020-12-17
  • 2021-01-31
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
相关资源
最近更新 更多