У меня есть таблица:
PersonID FirstName PersonAge
1 Pras 2
2 Deep 3
3 Test 4
4 Prash 2
5 ser 1
6 df 8
7 ddf 5
8 vvv 4
9 ddd 1
10 eww 6
11 vvv 3
12 vbbb 7
13 Prabbbbs 6
Я хочу сгруппировать их так, чтобы общий возраст группы не превышал 10.
Я могу сделать это с помощью рекурсии, но этонеэффективен для больших таблиц.
;WITH cte AS
(
SELECT PersonID, PersonAge, 1 AS [Group], PersonAge AS RunningTotal FROM POP where PersonId=1
UNION ALL
SELECT data.PersonId, data.PersonAge,
CASE WHEN cte.RunningTotal + data.PersonAge > 10 THEN cte.[Group] + 1 ELSE cte.[Group] END,
-- Reset the running total for each new group
data.PersonAge + CASE WHEN cte.RunningTotal + data.PersonAge > 10 THEN 0 ELSE cte.RunningTotal END
FROM POP data INNER JOIN cte ON data.PersonId = cte.PersonID + 1
)
SELECT * FROM cte
Итак, мне нужен вывод:
PersonID PersonAge Group RunningTotal
1 2 1 2
2 3 1 5
3 4 1 9
4 2 2 2
5 1 2 3
6 8 3 8
7 5 4 5
8 4 4 9
9 1 4 10
10 6 5 6
11 3 5 9
12 7 6 7
13 6 7 6
Есть ли хорошее нерекурсивное решение?
РЕДАКТИРОВАТЬ: Пробная версия #1. Думая по ходу промежуточных итогов, я получил таблицу с CurrRunningTotal и RunningTotal до предыдущей строки.
WITH TE
AS (SELECT
PersonId,
FirstName,
PersonAge,
SUM(PersonAge) OVER (ORDER BY PersonId
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
AS PrevRunningTotal,
SUM(PersonAge) OVER (ORDER BY PersonId
ROWS UNBOUNDED PRECEDING)
AS RunningTotal
FROM POP),
MergedGroup
AS (SELECT
*,
SUM(CASE
WHEN RunningTotal > @total THEN RunningTotal - @total
ELSE PersonAge
END) OVER (ORDER BY PersonId) AS Total
FROM TE)
SELECT
*
FROM MergedGroup
Я чувствую, что используя PreviousRunningTotal, я могу творить чудеса, когда мой порог достигнут, чтобы получить итогс заполнением, т. е. при превышении порога, добавьте 10 к текущей строке, чтобы компенсировать итоговое значение. До сих пор близко, но без сигары.