Как разбить записи на группы по сумме?

Есть таблица, отсортированная по полю kolich в обратном порядке:

id kolich
7863 5392
3230 2321
3821 1914
10595 1524
11397 1395
10602 952
10612 829
10611 828
10081 670

Как получить такую таблицу:

id kolich grupa
7863 5392 1
3230 2321 2
3821 1914 3
10595 1524 4
11397 1395 5
10602 952 6
10612 829 6
10611 828 7
10081 670 7

чтобы уникальная grupa присваивалась идущим подряд записям,сумма(kolich) которых меньше 2000?

То есть подряд идущие записи должны в сумме давать <2000, а если следующая запись превышает этот лимит, то ей присваивается новая группа на примере выше это группы (grupa) 6 и 7

Полагаю это можно реализовать оконной функцией с промежуточными итогами. Пока хожу возле:

SUM(count(*)) OVER (
    PARTITION BY case when count(*) < 2000 then id else count(*) end
    ORDER BY kolich rows between unbounded preceding and current row
)
AS rns

Ответы (2 шт):

Автор решения: rotabor

Предлагаю решение на c#:

using System;
using System.Linq;
using System.Collections.Generic;
class IdKolich {
    public int id, kolich, grupa;
    public IdKolich(int id, int kolich)
        { this.id = id; this.kolich = kolich; grupa = 0; }
    public override string ToString()
        { return string.Format("{0,10}{1,10}{2,10}", id, kolich, grupa); }
}
static class Program {
    static void Main(string[] args) {
        // тестовые данные
        var iks = new IdKolich[] { new IdKolich(7863, 5392),
            new IdKolich(3230, 2321),
            new IdKolich(3821, 1914),
            new IdKolich(10595, 1524),
            new IdKolich(11397, 1395),
            new IdKolich(10602, 952),
            new IdKolich(10612, 829),
            new IdKolich(10611, 828),
            new IdKolich(10081, 670) };
        // решение
        int i = iks[0].kolich >= 2000 ? 0 : 1;
        iks.Aggregate(0, (stot, cur) => {
                if ((stot += cur.kolich) >= 2000) { i++; stot = cur.kolich; }
                cur.grupa = i; return stot;
            } );
        // вавод результата
        foreach (var ik in iks) Console.WriteLine(ik);
    }
}

-->

      7863      5392         1
      3230      2321         2
      3821      1914         3
     10595      1524         4
     11397      1395         5
     10602       952         6
     10612       829         6
     10611       828         7
     10081       670         7
→ Ссылка
Автор решения: ValNik

Такая задача довольно часто встречается - набрать группы строк не больше определенного размера. Например, для заполнения грузов в контейнер или грузовик.
Как уже комментировал @Akina, нужен рекурсивный запрос.
Перебираем строки по одной в цикле (рекурсии).

Запрос достаточно простой, можете адаптировать под Oracle.

Посмотрите вариант решения (для Postgresql)

with recursive 
 t as(select *,row_number()over(order by kolich desc) rn from test)
,r as(
  select *
    ,kolich ts
    ,1 grn
  from t
  where rn=1
  union all
  select t.*
    ,case when (r.ts+t.kolich)>2000 then t.kolich
     else r.ts+t.kolich
     end ts
    ,case when (r.ts+t.kolich)>2000 then r.grn+1
     else r.grn
     end grn
  from r inner join t on (t.rn=r.rn+1)
)
select * from r order by rn;

Результат

id kolich rn ts grn
7863 5392 1 5392 1
3230 2321 2 2321 2
3821 1914 3 1914 3
10595 1524 4 1524 4
11397 1395 5 1395 5
10602 952 6 952 6
10612 829 7 1781 6
10611 828 8 828 7
10081 670 9 1498 7

Demo

→ Ссылка