在T-SQL中运行SUM

时间:2015-10-08 13:16:43

标签: sql sql-server tsql

对不好的主题感到抱歉,但我不知道该怎么称呼它。

我的桌子看起来像这样:

+-----++-----+
| Id  ||Count|
+-----++-----+
| 1   || 1   |
+-----++-----+
| 2   || 5   |
+-----++-----+
| 3   || 8   |
+-----++-----+
| 4   || 3   |
+-----++-----+
| 5   || 6   |
+-----++-----+
| 6   || 8   |
+-----++-----+
| 7   || 3   |
+-----++-----+
| 8   || 1   |
+-----++-----+

我正在尝试从这个表中进行选择,每次row1 + row2 + row3(等)的SUM达到10时,那么它就是“HIT”,并且计数重新开始。

请求的输出:

+-----++-----++-----+
| Id  ||Count|| HIT |
+-----++-----++-----+
| 1   || 1   ||  N  | Count = 1
+-----++-----++-----+
| 2   || 5   ||  N  | Count = 6
+-----++-----++-----+
| 3   || 8   ||  Y  | Count = 14 (over 10)
+-----++-----++-----+
| 4   || 3   ||  N  | Count = 3
+-----++-----++-----+
| 5   || 6   ||  N  | Count = 9
+-----++-----++-----+
| 6   || 8   ||  Y  | Count = 17 (over 10..)
+-----++-----++-----+
| 7   || 3   ||  N  | Count = 3
+-----++-----++-----+
| 8   || 1   ||  N  | Count = 4
+-----++-----++-----+

我该如何做到这一点,并且性能最佳?我不知道..

4 个答案:

答案 0 :(得分:4)

评论太长了。

您无法使用窗口/分析函数执行此操作,因为事先不知道断点。有时,可以计算断点。但是,在这种情况下,断点取决于先前值的非线性函数(我现在想不到比“非线性”更好的词)。也就是说,有时将“1”添加到较早的值对当前行的计算没有影响。有时它会产生很大的影响。这意味着计算必须从头开始并迭代数据。

使用此类函数可以解决 问题的微小修改。相反,如果问题是为每个组继承多余的金额(而不是重新开始总和),那么使用累积金额(以及其他一些技巧)可以解决问题。

递归查询(其他人提供的)或顺序操作是解决此问题的最佳方法。不幸的是,它没有基于集合的方法来解决它。

答案 1 :(得分:3)

您可以使用Recursive Queries

请注意以下查询,假设id值全部按顺序排列,否则请使用ROW_NUMBER()创建新ID

WITH cte AS (
  SELECT id, [Count], [Count] AS total_count
  FROM Table1 
  WHERE id = 1
  UNION ALL
  SELECT t2.id,t2.[Count], CASE WHEN t1.total_count >= 10 THEN t2.[Count] ELSE t1.total_count + t2.[Count] END
  FROM Table1 t2
  INNER JOIN cte t1 
    ON t2.id = t1.id + 1
  )
SELECT *
FROM cte
ORDER BY id

SQL Fiddle

答案 2 :(得分:1)

我真的希望有人能告诉我们是否可以使用直接窗口功能来实现这一目标。这是真正的挑战。

与此同时,我将使用递归来实现它。这会处理序列中的间隙,并处理第一行已经为>= 10的边缘情况。

我还添加了maxrecursion提示以删除默认的递归限制。但老实说,我不知道它会在大量数据的情况下运行得多好。

with NumberedRows as (
  select Id, Cnt,
         row_number() over(order by id) as rn
    from CountTable
), RecursiveCTE as (
  select Id, Cnt, rn, 
         case when Cnt >= 10 then 0 else Cnt end as CumulativeSum,
         case when Cnt >= 10 then 'Y' else 'N' end as hit
    from NumberedRows
   where rn = 1
  union all
  select n.Id, n.Cnt, n.rn,
         case when (n.Cnt + r.CumulativeSum) >= 10 then 0 else n.Cnt + r.CumulativeSum end as CumulativeSum,
         case when (n.Cnt + r.CumulativeSum) >= 10 then 'Y' else 'N' end as hit
    from RecursiveCTE r
    join NumberedRows n
      on n.rn = r.rn + 1
)
select Id, Cnt, hit
from RecursiveCTE
order by Id
option (maxrecursion 0)

SQLFiddle Demo

答案 3 :(得分:1)

使用Running Totals:

DECLARE @Data TABLE(
    Id INT
    ,SubTotal INT
)


INSERT INTO @Data
    VALUES(1, 5)

INSERT INTO @Data
    VALUES(2, 3)

INSERT INTO @Data
    VALUES(3, 4)

INSERT INTO @Data
    VALUES(4, 4)

INSERT INTO @Data
    VALUES(5, 7)

DECLARE @RunningTotal INT = 0
DECLARE @HitCount INT = 0    

SELECT  
        @RunningTotal = CASE WHEN @RunningTotal < 10 THEN @RunningTotal + SubTotal ELSE SubTotal END
        ,@HitCount = @HitCount + CASE WHEN @RunningTotal >= 10 THEN 1 ELSE 0 END
        FROM @Data ORDER BY Id

SELECT @HitCount -- Outputs 2

重新阅读这个问题,我发现这不符合要求的输出 - 我会留下答案,因为它可能会对寻找这类问题的运行整体解决方案的例子有所帮助不需要用Y或N标记每一行。