我有桌子:
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添加到当前行以抵消总数。接近但没有雪茄。
答案 0 :(得分:1)
评论太长了。
是的,您可以轻松地满足您的条件,但不是最佳的。只需按{{1}}分组。
据推测,你打算更喜欢"从头开始并使用相邻记录"或者"最小化群组的数量"。后者是一个bin-packing问题,并且没有已知的算法存在有效的性能。
前者 - 遗憾的是 - 需要从一开始就循环访问数据,这就是递归CTE所做的事情。我不知道有任何其他通用方法来解决问题。可能有特定的方法,具体取决于您问题的确切限制。