I am trying to write a query whose purpose is to group multiple sequential rows for future processing. The rules for such grouping are:
- Each row has a segment identifier and a corresponding weight.
- As many contiguous segments as possible must be grouped together, provided that their total weight does not exceed the specified threshold.
- If a segment has weight exceeding the specified threshold, the resulting group will only represent that segment.
Here is an example:
SET NOCOUNT ON;
DROP TABLE IF EXISTS #tmpIncoming;
CREATE TABLE #tmpIncoming
(
[Segment] int NOT NULL,
[Weight] int NOT NULL
);
INSERT INTO #tmpIncoming VALUES
( 1, 25),
( 2, 45),
( 3, 20),
( 4, 30),
( 5, 50),
( 6, 21),
( 7, 110);
DECLARE @nMaxChunkSize int = 100;
-- BEGIN: suboptimal
DROP TABLE IF EXISTS #tmpResult;
CREATE TABLE #tmpResult
(
[MinSegment] int NOT NULL,
[MaxSegment] int NOT NULL,
[Weight] int NOT NULL
);
DECLARE cur CURSOR LOCAL READ_ONLY FORWARD_ONLY STATIC FOR
SELECT
[Segment],
[Weight]
FROM
#tmpIncoming
ORDER BY
[Segment];
OPEN cur;
DECLARE @nMinSegment int = 0, @nMaxSegment int = 0;
DECLARE @nWeightSoFar int = 0;
WHILE (1=1)
BEGIN
DECLARE @nSegment int, @nWeight int;
FETCH NEXT FROM cur INTO @nSegment, @nWeight;
IF (@@FETCH_STATUS <> 0)
BREAK;
IF (@nWeightSoFar + @nWeight > @nMaxChunkSize)
BEGIN
INSERT INTO #tmpResult ([MinSegment], [MaxSegment], [Weight])
VALUES (@nMinSegment, @nMaxSegment, @nWeightSoFar);
SET @nMinSegment = @nSegment;
SET @nMaxSegment = @nSegment;
SET @nWeightSoFar = @nWeight;
END
ELSE
BEGIN
IF (@nMinSegment = 0)
SET @nMinSegment = @nSegment;
SET @nMaxSegment = @nSegment;
SET @nWeightSoFar = @nWeightSoFar + @nWeight;
END;
END;
CLOSE cur;
DEALLOCATE cur;
IF (@nWeightSoFar > 0)
INSERT INTO #tmpResult ([MinSegment], [MaxSegment], [Weight])
VALUES (@nMinSegment, @nMaxSegment, @nWeightSoFar);
SELECT * FROM #tmpResult;
DROP TABLE IF EXISTS #tmpResult;
-- END: suboptimal
DROP TABLE IF EXISTS #tmpIncoming;
I was only able to think of a suboptimal implementation which uses a cursor variable. Can anyone recommend a better approach, preferably with only one SELECT and maybe some CTE?