每次总和低于 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 分组的主要内容,如果未能解决你的问题,请参考以下文章

PySpark:如何在列中使用 Or 进行分组

Oracle SQL 行到具有分组依据和总和的列

oracle SQL 取出每个分组的按照日期最新一条记录,同时还显示每个分组某个字段的总和

将查询分组为 3 列,并在 Apex Oracle 中显示另一列的总和

按总和条件分组[重复]

Linq:按 OR 条件分组