I need to group rows by some columns and by a running sum until it reaches a threshold. The closest I got was with a query based on this answer, but this solution is not as precise as I need it to be, because the sum has to be reset and restarted when it reaches the threshold.
Here's my attempt with some sample data and a threshold of 100:
drop table #table
create table #table (
Id int not null,
GroupId int not null,
Code nvarchar(14) not null,
Total int not null
)
insert into #table values
( 1, 1, '1111', 20),( 2, 1, '1111', 75),( 3, 1, '1111', 40),( 4, 1, '1111', 20),
( 5, 1, '1111', 20),( 6, 1, '1111', 25),( 7, 1, '2222', 20),( 8, 1, '2222', 20),
( 9, 1, '2222', 20),(10, 1, '2222', 20),(11, 2, '3333', 10),(12, 2, '3333', 90),
(13, 2, '3333', 90),(14, 2, '3333', 90),(15, 2, '3333', 90),(16, 2, '3333', 10),
(17, 2, '3333', 10),(18, 2, '3333', 10),(19, 2, '3333', 10),(20, 2, '3333', 10),
(21, 2, '3333', 10),(22, 2, '3333', 10),(23, 2, '3333', 10),(24, 2, '3333', 10),
(25, 2, '3333', 10),(26, 2, '3333', 10),(27, 2, '3333', 10),(28, 2, '3333', 10),
(29, 2, '3333', 10),(30, 2, '3333', 10),(31, 2, '3333', 10),(32, 2, '3333', 10),
(33, 2, '3333', 10),(34, 2, '3333', 10),(35, 2, '3333', 10)
;with cte as (
select
Id, GroupId, Code, Total,
cast(sum(Total) OVER (ORDER BY Code, id) as int) / 100 AS Limit
from #table
)
select
*,
dense_rank() over(ORDER BY Code, Limit) as Groups
from cte order by id
This is what I get, I manually added a "GroupsExpected" column to show what I actually need the groups to be.
| Id | GroupId | Code | Total | Limit | Groups | GroupsExpected |
|----|---------|------|-------|-------|--------|----------------|
| 1 | 1 | 1111 | 20 | 0 | 1 | 1 |
| 2 | 1 | 1111 | 75 | 0 | 1 | 1 |
| 3 | 1 | 1111 | 40 | 1 | 2 | 2 |
| 4 | 1 | 1111 | 20 | 1 | 2 | 2 |
| 5 | 1 | 1111 | 20 | 1 | 2 | 2 |
| 6 | 1 | 1111 | 25 | 2 | 3 | 3 |
| 7 | 1 | 2222 | 20 | 2 | 4 | 4 |
| 8 | 1 | 2222 | 20 | 2 | 4 | 4 |
| 9 | 1 | 2222 | 20 | 2 | 4 | 4 |
| 10 | 1 | 2222 | 20 | 2 | 4 | 4 |
| 11 | 2 | 3333 | 10 | 2 | 5 | 5 |
| 12 | 2 | 3333 | 90 | 3 | 6 | 6 |
| 13 | 2 | 3333 | 90 | 4 | 7 | 7 |
| 14 | 2 | 3333 | 90 | 5 | 8 | 8 |
| 15 | 2 | 3333 | 90 | 6 | 9 | 9 |
| 16 | 2 | 3333 | 10 | 6 | 9 | 10 |
| 17 | 2 | 3333 | 10 | 6 | 9 | 10 |
| 18 | 2 | 3333 | 10 | 6 | 9 | 10 |
| 19 | 2 | 3333 | 10 | 6 | 9 | 10 |
| 20 | 2 | 3333 | 10 | 7 | 10 | 10 |
| 21 | 2 | 3333 | 10 | 7 | 10 | 10 |
| 22 | 2 | 3333 | 10 | 7 | 10 | 10 |
| 23 | 2 | 3333 | 10 | 7 | 10 | 10 |
| 24 | 2 | 3333 | 10 | 7 | 10 | 10 |
| 25 | 2 | 3333 | 10 | 7 | 10 | 11 |
| 26 | 2 | 3333 | 10 | 7 | 10 | 11 |
| 27 | 2 | 3333 | 10 | 7 | 10 | 11 |
| 28 | 2 | 3333 | 10 | 7 | 10 | 11 |
| 29 | 2 | 3333 | 10 | 7 | 10 | 11 |
| 30 | 2 | 3333 | 10 | 8 | 11 | 11 |
| 31 | 2 | 3333 | 10 | 8 | 11 | 11 |
| 32 | 2 | 3333 | 10 | 8 | 11 | 11 |
| 33 | 2 | 3333 | 10 | 8 | 11 | 11 |
| 34 | 2 | 3333 | 10 | 8 | 11 | 12 |
| 35 | 2 | 3333 | 10 | 8 | 11 | 12 |
The sum of the Total
column for each group can't hit 100, and groups '9' and '10' hit that amount (their sum is 130 and 100 respectively).
I also tried to use a recursive CTE from this answer, but in that case I couldn't group by the Code
column.
I could do this programmatically, but I have a feeling this can be achieved more easily with a single query.
I'm using MSSQL 2016.