每次总和低于 15 时进行 oracle 分组
Posted
技术标签:
【中文标题】每次总和低于 15 时进行 oracle 分组【英文标题】:oracle grouping everytime the sum amount is below 15 【发布时间】:2021-01-14 10:51:40 【问题描述】:我有以下数据:
SELECT 1 note, 1000 amt FROM dual union all
SELECT 2 note, 2000 amt FROM dual union all
SELECT 3 note, 8000 amt FROM dual union all
SELECT 4 note, 3000 amt FROM dual union all
SELECT 5 note, 1500 amt FROM dual union all
SELECT 6 note, 1600 amt FROM dual union all
SELECT 7 note, 20000 amt FROM dual union all
SELECT 8 note, 20000 amt FROM dual union all
SELECT 9 note, 2100 amt FROM dual union all
SELECT 10 note, 4500 amt FROM dual union all
SELECT 11 note, 1000 amt FROM dual union all
SELECT 12 note, 16000 amt FROM dual
我需要总和,但是对于每个总和 15000,他们将像这样在自己的组中:
NOTE | AMT | group |
---|---|---|
1 | 1000 | 1 |
11 | 1000 | 1 |
5 | 1500 | 1 |
6 | 1600 | 1 |
2 | 2000 | 1 |
9 | 2100 | 1 |
4 | 3000 | 1 |
10 | 4500 | 2 |
3 | 8000 | 2 |
12 | 16000 | 3 |
7 | 20000 | 4 |
8 | 20000 | 5 |
我需要oracle sql中的解决方案,可以吗?我正在使用 oracle 11g
【问题讨论】:
【参考方案1】:一种方法是递归子查询:
with tt(note, amt, seqnum) as (
select t.note, t.amt, row_number() over (order by amt) as seqnum
from t
),
cte(note, amt, seqnum, grp, running_amt) as (
select note, amt, seqnum, 1, amt
from tt
where seqnum = 1
union all
select tt.note, tt.amt, tt.seqnum,
(case when tt.amt + cte.running_amt > 15000 then cte.grp + 1 else cte.grp end),
(case when tt.amt + cte.running_amt > 15000 then tt.amt else tt.amt + cte.running_amt end)
from cte join
tt
on tt.seqnum = cte.seqnum + 1
)
select *
from cte;
Here 是一个 dbfiddle。
注意:这是按amt
排序的——与您的示例数据一样。您可以通过在tt
中调整seqnum
轻松地通过note
订购(这也很有意义)。
【讨论】:
哇,谢谢!我什至不这么想。一个问题,性能方面,这种方法会占用大量资源吗? 我认为由于自加入和联合,理论上这可能比普通的 MATCH_RECOGNIZE 慢一些。但很可能您不会注意到任何差异。 取决于“Oracle 11g”是指版本 11.1 还是 11.2 递归子查询也不可用。它们已在 11.2 中引入。 我明白了,实际上我使用的是 11.2,但不知何故 MATCH_RECOGNIZE 在我的系统中不起作用。我想我会坚持你的解决方案。谢谢! @rgprakoso 。 . .您应该接受其中一个答案。【参考方案2】:您可以使用SQL for Pattern Matching:
WITH t AS (
SELECT 1 note, 1000 amt FROM DUAL UNION ALL
SELECT 2 note, 2000 amt FROM DUAL UNION ALL
SELECT 3 note, 8000 amt FROM DUAL UNION ALL
SELECT 4 note, 3000 amt FROM DUAL UNION ALL
SELECT 5 note, 1500 amt FROM DUAL UNION ALL
SELECT 6 note, 1600 amt FROM DUAL UNION ALL
SELECT 7 note, 20000 amt FROM DUAL UNION ALL
SELECT 8 note, 20000 amt FROM DUAL UNION ALL
SELECT 9 note, 2100 amt FROM DUAL UNION ALL
SELECT 10 note, 4500 amt FROM DUAL UNION ALL
SELECT 11 note, 1000 amt FROM DUAL UNION ALL
SELECT 12 note, 16000 amt FROM DUAL)
SELECT note, amt, amt_group, sum_amt
FROM t
MATCH_RECOGNIZE (
ORDER BY amt
MEASURES
MATCH_NUMBER() AS amt_group,
NVL(SUM(amt), amt) AS sum_amt
ALL ROWS PER MATCH
PATTERN (s*)
DEFINE
s AS SUM(amt) <= 15000
) mr
ORDER BY amt
NOTE | AMT | AMT_GROUP | SUM_AMT |
---|---|---|---|
1 | 1000 | 1 | 1000 |
11 | 1000 | 1 | 2000 |
5 | 1500 | 1 | 3500 |
6 | 1600 | 1 | 5100 |
2 | 2000 | 1 | 7100 |
9 | 2100 | 1 | 9200 |
4 | 3000 | 1 | 12200 |
10 | 4500 | 2 | 4500 |
3 | 8000 | 2 | 12500 |
12 | 16000 | 3 | 16000 |
7 | 20000 | 4 | 20000 |
8 | 20000 | 5 | 20000 |
【讨论】:
嘿,谢谢你的回复,我只知道这个 match_recognized 的事情。但是当我在我的服务器上尝试它时,它总是返回 ORA-00933 的错误。知道为什么吗?我用的是 11g MATCH_RECOGNIZE 是在 Oracle 12.1 中引入的。同时Oracle 11g已经过时,也许是时候升级了。 啊,我明白了,这就是我得到的错误的解释。感谢您的信息!以上是关于每次总和低于 15 时进行 oracle 分组的主要内容,如果未能解决你的问题,请参考以下文章
oracle SQL 取出每个分组的按照日期最新一条记录,同时还显示每个分组某个字段的总和