PostgreSQL中如何对非连续数值分组求MIN()与MAX()?
识别连续数值段的SQL解决方案
这是个很常见的连续数值分组问题——常规的MIN/MAX聚合会把整个分组的数值范围合并,完全掩盖中间的间隙。我们可以借助窗口函数来标记出每个连续的数值段,再进行聚合就能得到你想要的结果。
核心思路
连续的数值有个关键特点:如果在目标分组内按number排序,用number减去它的行号,得到的差值会是固定的。比如你的示例数据:
- 第一组连续数19988-19991:19988-1=19987,19989-2=19987,…,19991-4=19987,差值完全一致
- 第二组22001-22002:22001-5=21996,22002-6=21996,差值一致
- 第三组26007-26010同理,差值也会统一
这个差值就是我们用来区分不同连续段的标记,基于它再和你的目标分组字段一起聚合,就能得到每个段的起止数值和数量。
完整SQL语句
假设你的表名为transaction_details,可以用如下查询:
WITH segment_marker AS ( SELECT parent_id, transaction_code, way_to_pay, type_of_receipt, unit_price, period, series, number, -- 计算连续段的分组标记 number - ROW_NUMBER() OVER ( PARTITION BY parent_id, transaction_code, way_to_pay, type_of_receipt, unit_price, period, series ORDER BY number ) AS group_id FROM transaction_details ) SELECT parent_id, transaction_code, way_to_pay, type_of_receipt, unit_price, period, series, MIN(number) AS number_from, MAX(number) AS number_to, COUNT(number) AS total_numbers FROM segment_marker GROUP BY parent_id, transaction_code, way_to_pay, type_of_receipt, unit_price, period, series, group_id ORDER BY parent_id, number_from;
语句解释
- CTE部分(segment_marker):给每个目标分组(你指定的7个字段)内的行按
number排序,计算group_id作为连续段的唯一标记。 - 外层聚合:用原分组字段+
group_id分组,聚合得到每个连续段的起始值(MIN(number))、结束值(MAX(number))和数量(COUNT(number))。 - 排序:最后按
parent_id和number_from排序,让结果和你期望的格式完全匹配。
把你的示例数据代入这个查询,就能精准得到你想要的三个连续段的结果。
内容的提问来源于stack exchange,提问作者Mandres
相关产品推荐
相关产品推荐

