【问题标题】:Running SUM in T-SQL在 T-SQL 中运行 SUM
【发布时间】:2016-01-06 03:32:13
【问题描述】:

抱歉,主题不好,但我不知道该怎么称呼它..

我有一张这样的表:

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

我正在尝试从该表中进行选择,其中每次 row1 + row2 + row3(等)的总和达到 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
+-----++-----++-----+

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

【问题讨论】:

  • 使用dense_rank()的有趣想法。也许这会奏效。否则我担心可能需要一个带有游标的存储过程。
  • 如果dense_rank() 不能解决问题,您也可以使用递归视图来实现。我相信dense_rank() 路由会得到更好的优化。
  • 这张桌子很大..我会试试看是否可以使用dense_rank
  • 同意 100%。如果表是数千个,那么没什么大不了的,但如果它更大,那么递归将在这里表现不佳。

标签: sql sql-server tsql


【解决方案1】:

你可以使用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】:

    我真的希望有人可以向我们展示是否可以使用直接的窗口函数来做到这一点。这才是真正的挑战。

    同时,这是我使用递归的方法。这会处理序列中的间隙,并处理第一行的边缘情况已经是>= 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”添加到较早的值对当前行的计算具有零影响。有时它会产生很大的影响。这意味着计算必须从头开始并遍历数据。

      对问题的小修改可以使用这些函数来解决。相反,如果问题是结转每组的超额金额(而不是重新计算总和),则可以使用累积总和(以及其他一些技巧)来解决问题。

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

      【讨论】:

        【解决方案4】:

        如何使用运行总计:

        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 标记的每一行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多