从 SPL 到 SQL:灵活区间汇总

Oracle 数据库某表的 id 字段的范围是整数 1-2000。

id value
1 10
1 11
2 100
3 5
3 9
20 0
21 1000
22 2000
1000 1
1000 20

现在要对 id 按多个连续的区间进行分组,对组内的 value 进行求和,计算结果按区间顺序排列。区间是数字序列的形式,可以灵活修改。比如数字序列是 10,20,100,1000,2000 时,表示将 id 按 1-10、11-20、21-100、101-1000、1001-2000 的区间进行分组汇总。注意分组字符串的起始值的格式是:数字序列的成员 +1

IDs_range sum_of_values
1-10 135
11-20 0
21-100 3000
101-1000 21

SQL:


WITH id_ranges (min_value, max_value) AS (
  SELECT LAG(COLUMN_VALUE, 1, 0) OVER (ORDER BY COLUMN_VALUE) + 1,
         COLUMN_VALUE
  FROM   TABLE(SYS.ODCINUMBERLIST(10,20,100,1000,2000))
)
SELECT r.min_value || '-' || r.max_value AS id_range,
       SUM(value) AS sum_of_values
FROM   dummy_data d
       INNER JOIN id_ranges r
       ON     d.id BETWEEN r.min_value AND r.max_value
GROUP BY r.min_value, r.max_value
order by  r.min_value

SQL 没有提供求某个值在哪个区间的函数,不能直接按区间分组,要绕大弯造个临时的区间表,关联后再分组汇总,结构比较复杂。

SPL 代码要简单易懂很多: https://try.esproc.com/splx?3KN


 A
1 $select * from dummy_data.txt order by id
2 =list=[10,20,100,1000,2000]
3 =A1.groups@u(list.pseg@r(id):IDs_range; sum(value):sum_of_values)
4 =A3.run(IDs_range=(ifn(list.m(IDs_range),0)+1) / "-" / list.m(IDs_range+1))

A1:加载数据。

A3:直接按区间分组汇总,函数 pseg 返回某个值所在的区间序号,不需要造表,也不用关联,@r 表示左开右闭。

A4:将分组号改成指定的字符串格式。

问题来源:https://stackoverflow.com/questions/78260383/how-to-group-by-over-a-range-range-values-are-defined-in-range-table