SQL运行总计组并重置阈值阈值

时间:2018-05-24 19:37:35

标签: sql sql-server window-functions ranking-functions partition-by

我有桌子:

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添加到当前行以抵消总数。接近但没有雪茄。

1 个答案:

答案 0 :(得分:1)

评论太长了。

是的,您可以轻松地满足您的条件,但不是最佳的。只需按{{1}}分组。

据推测,你打算更喜欢"从头开始并使用相邻记录"或者"最小化群组的数量"。后者是一个bin-packing问题,并且没有已知的算法存在有效的性能。

前者 - 遗憾的是 - 需要从一开始就循环访问数据,这就是递归CTE所做的事情。我不知道有任何其他通用方法来解决问题。可能有特定的方法,具体取决于您问题的确切限制。