Мне нужно сгруппировать набор строк на основе столбца Category
, а также ограничить объединенные строки на основе столбца SUM(Number)
, чтобы они были меньше или равны значению @Limit
.
Для каждого отдельного столбца Category
мне нужно идентифицировать "сегменты", которые <= <code>@limit. Если сумма (Number
) всех строк для столбца Category
равна <= <code>@Limit, тогда для этого значения Category
будет только 1 интервал (например, 'CCCC' в данных примера). Однако если SUM (Number
)> @limit
, то для этого значения Category
будет несколько строк сегмента (например, «AAAA» в данных примера), и каждый интервал должен быть <= <code>@Limit. Там может быть столько ведер, сколько необходимо. Кроме того, посмотрите на Category
значение 'DDDD', его одна строка больше, чем @Limit
сама по себе, и разбивается на две строки в наборе результатов.
Учитывая эти упрощенные данные:
DECLARE @Detail table (DetailID int primary key, Category char(4), Number int)
SET NOCOUNT ON
INSERT @Detail VALUES ( 1, 'AAAA',100)
INSERT @Detail VALUES ( 2, 'AAAA', 50)
INSERT @Detail VALUES ( 3, 'AAAA',300)
INSERT @Detail VALUES ( 4, 'AAAA',200)
INSERT @Detail VALUES ( 5, 'BBBB',500)
INSERT @Detail VALUES ( 6, 'CCCC',200)
INSERT @Detail VALUES ( 7, 'CCCC',100)
INSERT @Detail VALUES ( 8, 'CCCC', 50)
INSERT @Detail VALUES ( 9, 'DDDD',800)
INSERT @Detail VALUES (10, 'EEEE',100)
INSERT @Detail VALUES (11, 'AAAA',200) --EDIT added
INSERT @Detail VALUES (12, 'AAAA',200) --EDIT added
INSERT @Detail VALUES (13, 'AAAA',200) --EDIT added
INSERT @Detail VALUES (14, 'AAAA',200) --EDIT added
SET NOCOUNT OFF
DECLARE @Limit int
SET @Limit=500
Мне нужен один из следующих наборов результатов:
DetailID Bucket | DetailID Category Bucket
-------- ------ | -------- -------- ------
1 1 | 1 'AAAA' 1
2 1 | 2 'AAAA' 1
3 1 | 3 'AAAA' 1
4 2 | 4 'AAAA' 2
11 2 | 11 'AAAA' 2 --EDIT added
12 3 | 12 'AAAA' 3 --EDIT added
13 3 | 13 'AAAA' 3 --EDIT added
14 4 | 14 'AAAA' 4 --EDIT added
5 5 OR 5 'BBBB' 1
6 6 | 6 'CCCC' 1
7 6 | 7 'CCCC' 1
8 6 | 8 'CCCC' 1
9 7 | 9 'DDDD' 1
9 8 | 9 'DDDD' 2
10 9 | 10 'EEEE' 1
РЕДАКТИРОВАТЬ после опробования всех ответов
Поскольку все попытки решения, основанного на множествах, не работают должным образом, я собираюсь внести изменение в @ GalacticJello Answer , изменение, указанное в приведенном ниже коде. Я в основном нахожу все строки, где вся категория помещается в корзину, и вставляю их, используя один INSERT-SELECT, а затем перебираю оставшиеся данные, используя цикл без курсора @GalacticJello. В моей ситуации это будет хорошо работать, так как цикл вряд ли когда-либо будет обрабатываться.
DECLARE @DetailTemp table (PID INT IDENTITY(1,1), DetailID int primary key, Category char(4), Number int)
DECLARE @DetailFinal table (DetailID int, Category char(4), Bucket int) ---<<<renamed column to Bucket
DECLARE @DetailCount int
SET @DetailCount = 0;
--------<<<optimization added starts here
;WITH AllSingleBuckets AS (
SELECT
Category
FROM @Detail
GROUP BY Category
HAVING SUM(Number)<=@Limit
)
INSERT INTO @DetailFinal
(DetailID, Category, Bucket)
SELECT
d.DetailID,d.Category,1
FROM @Detail d
INNER JOIN AllSingleBuckets s ON d.Category=s.Category
--------<<<optimization added ends here
INSERT @DetailTemp
--------<<<changed for optimization, added WHERE clause
SELECT d.DetailId, d.Category, d.Number FROM @Detail d WHERE NOT EXISTS (SELECT 1 FROM @DetailFinal f WHERE d.Category=f.Category) ORDER BY Category, DetailId
SELECT @DetailCount = @@ROWCOUNT
DECLARE @CurrentPid int
SET @CurrentPid = 1
DECLARE @ThisId int
DECLARE @ThisCategory char(4)
DECLARE @ThisNumber int
DECLARE @CurrentCategory char(4)
DECLARE @CurrentSum INT
DECLARE @CurrentBucket INT
WHILE @CurrentPid <= @DetailCount
BEGIN
SELECT @ThisId = DetailId, @ThisCategory = Category, @ThisNumber = Number
FROM @DetailTemp
WHERE PID = @CurrentPid
IF @ThisCategory = @CurrentCategory
BEGIN
IF @CurrentSum + @ThisNumber > @Limit
BEGIN
SET @CurrentBucket = @CurrentBucket + 1
SET @CurrentSum = @ThisNumber
END
ELSE
BEGIN
SET @CurrentSum = @CurrentSum + @ThisNumber
END
END
ELSE
BEGIN
SET @CurrentBucket = 1
SET @CurrentCategory = @ThisCategory
SET @CurrentSum = @ThisNumber
END
WHILE @CurrentSum > @Limit
BEGIN
INSERT @DetailFinal SELECT @ThisId, @CurrentCategory, @CurrentBucket
SET @CurrentBucket = @CurrentBucket + 1
SET @CurrentSum = @CurrentSum - @Limit
END
INSERT @DetailFinal SELECT @ThisId, @CurrentCategory, @CurrentBucket
SET @CurrentPid = @CurrentPid + 1
END
SELECT * from @DetailFinal ORDER BY Category --------<<<added order by
ВЫВОД:
DetailID Category Bucket
----------- -------- -----------
1 AAAA 1
2 AAAA 1
3 AAAA 1
4 AAAA 2
11 AAAA 2
12 AAAA 3
13 AAAA 3
14 AAAA 4
5 BBBB 1
6 CCCC 1
7 CCCC 1
8 CCCC 1
9 DDDD 1
9 DDDD 2
10 EEEE 1
(15 row(s) affected)