【问题标题】:sql dynamic cumulative sum - SQL Serversql动态累计和 - SQL Server
【发布时间】:2018-07-08 00:20:42
【问题描述】:

我有一张这样的桌子

+------+--------+
|   ID | Salary | 
+------+--------+
| 1    |    100 |
| 2    |     40 |
| 3    |     30 |
| 4    |     40 |
| 5    |     90 |
| 6    |    160 |
| 7    |     70 |
| 8    |     40 |
| 9    |     20 |
| 10   |     10 |
| 11   |    200 | 
| 12   |     50 |
+------+--------+

我可以进行正常的累积总和,但我需要不同于正常的CUMULATIVE SUM。如果累计和%50高于上一次累计,则下一次累计累计从该值开始

+------+--------+---------------+
|   ID | Salary | Running Total |
+------+--------+---------------+
| 1    |    100 |           100 |
| 2    |     40 |           140 |
| 3    |     30 |           170 |
| 4    |     40 |           210 |
| 5    |     90 |           300 |
| 6    |    160 |           460 |
| 7    |     70 |           230 |
| 8    |     40 |           270 |
| 9    |     20 |           290 |
| 10   |     10 |           300 |
| 11   |    200 |           500 |
| 12   |     50 |           250 |
+------+--------+---------------+

我想要这样的输出。

【问题讨论】:

  • 嗨。您能否就您要达到的目标添加一些示例?像 input output 这样我们可以更好地理解。由于这个问题很难理解。
  • 460 > 1.5 * 300 是“重置”并从 160 开始的原因吗?请确认,否则添加更好的解释+更多示例。
  • 您能解释一下为什么您的运行总数会在ID=6 而不是ID=2 重置。你的 SQL 版本是什么?
  • 我想检查增量率。对我来说重要的是最后两个运行总数之间的关系。
  • 解决这个问题需要递归 CTE。这些都相当昂贵,而且您目前不了解。在提供答案之前,我想确保这个问题很清楚。你能解释一下为什么 50% 以及为什么第 (6) 行与之前的值而不是下一个值结合在一起吗?

标签: sql sql-server moving-average cumulative-sum


【解决方案1】:

基于递归 CTE 的答案

select * 
from @T t
order by t.id;
with cteRN as 
( select t.*, ROW_NUMBER() over (order by t.id) as rn 
  from @T t
)
, cte as 
( select t.id, t.salary, t.salary as running, flag = 0, t.rn
  from cteRN t
  where t.rn = 1
  union all 
  select t.id, t.salary
       , case when c.flag = 1 then t.salary + c.salary
              else                 c.running + t.salary
         end 
       , case when t.salary > c.running / 2 then 1
              else 0
         end
       , t.rn
  from cte c 
  join cteRN t
    on t.rn = c.rn + 1
)
select t.id, t.salary, t.running 
from cte t
order by t.id;

id          salary
----------- -----------
1           100
2           40
3           30
4           40
5           90
6           160
7           70
8           40
9           20
10          10
11          200
12          50

id          running     running
----------- ----------- -----------
1           100         100
2           140         140
3           170         170
4           210         210
5           300         300
6           460         460
7           230         230
8           270         270
9           290         290
10          300         300
11          500         500
12          250         250

【讨论】:

    【解决方案2】:

    顺便说一句,我如何将 RunTotal 列更改为移动平均列。我想计算该行到该行的总计平均值

    对于相同值的示例;

    +------+--------+----------+-----+
    |   ID | Salary |MovingAvg | Flag|/* if the movavg increasing rate higher */
    +------+--------+----------+-----+/* than %50 get 1 to flag, else 0 */
    | 1    |    100 |      100 |  0  |
    | 2    |     40 |       70 |  0  |/* (100+40)/2=70 */
    | 3    |     30 |     56.6 |  0  |/* (100+40+30)/3=56.6 */
    | 4    |     40 |     52.5 |  0  |/* (100+40+30+40)/4=52.5 */
    | 5    |     90 |       60 |  0  |/* (100+40+30+40+90)/5=60 */
    | 6    |    270 |       95 |  1  |/*MovAvg increasing rate is more than%50*/
    | 7    |     70 |       70 |  0  |/* starting from this line*/
    | 8    |     40 |       55 |  0  |/* (70+40)/2 */
    | 9    |     10 |       40 |  0  |
    | 10   |     60 |       45 |  0  |
    | 11   |    300 |       96 |  1  |/*MovAvg increasing rate is more than%50*/
    | 12   |     50 |       50 |  0  |
    | 13   |     60 |       55 |  0  |/* (50+60)/2 */
    +------+--------+----------+-----+
    

    【讨论】:

    • 当我向表中添加新值时,我遇到了一些问题,你知道吗? @DineshDB
    • 检查答案。希望对您有所帮助。
    • 这正是我想要的@DineshDB
    • 很高兴为您提供帮助。始终保持投票以鼓励答案。
    • 很高兴见到你@Gork。再见。
    【解决方案3】:

    第二个问题的答案:

    DECLARE @MIN INT, @MAX INT, @PreVal INT, @CurVal INT, @OldVal INT,@NextVal INT, @ExistVal INT
    DECLARE @TABLE1 TABLE(ID INT, Sal INT, RunTotal INT,TimesGen INT)
    
    INSERT INTO @TABLE1 (Id, Sal)
    SELECT * FROM @TAB
    
    SELECT @MIN=MIN(ID),@MAX=MAX(ID) FROM @TAB
    
    WHILE(@MIN<=@MAX)
    BEGIN
        SELECT @NextVal=SALARY FROM @TAB WHERE ID=@MIN
        SELECT @ExistVal=SALARY FROM @TAB WHERE ID=@MIN-1
        SELECT @OldVal=RunTotal FROM @TABLE1 WHERE ID=@MIN-1
        SELECT @PreVal=RunTotal FROM @TABLE1 WHERE ID=@MIN-2
    
        IF(@OldVal>@PreVal+(@PreVal/2))
        BEGIN
            SELECT @CurVal = @NextVal--+@ExistVal
            UPDATE @TABLE1 SET TimesGen=1 WHERE ID=@MIN 
        END
        ELSE
        BEGIN
            SELECT @CurVal=ISNULL(@OldVal,0)+@NextVal-- FROM @TABLE1 WHERE ID=@MIN
            UPDATE @TABLE1 SET TimesGen=ISNULL((SELECT TimesGen FROM @TABLE1 WHERE ID=@MIN-1),0)+1 WHERE ID=@MIN
        END
    
        UPDATE @TABLE1 SET RunTotal=@CurVal WHERE ID=@MIN
        SELECT @MIN=@MIN+1
    END
    
    SELECT ID,Sal Salary
        ,'('+CAST(RunTotal AS VARCHAR)+'/'+CAST(TimesGen AS VARCHAR)+') '+CAST(CAST(RunTotal*1.0/TimesGen AS NUMERIC(8,1)) AS VARCHAR) MovingAvg
        ,CAST(RunTotal*1.0/TimesGen AS NUMERIC(8,1))MovingAvg1
    FROM @TABLE1
    

    输出:

    ID  Salary  MovingAvg
    1   100     (100/1) 100.0
    2   40      (140/2) 70.0
    3   30      (170/3) 56.7
    4   40      (210/4) 52.5
    5   90      (300/5) 60.0
    6   160     (460/6) 76.7
    7   70      (230/2) 115.0
    8   40      (270/3) 90.0
    9   20      (290/4) 72.5
    10  10      (300/5) 60.0
    11  200     (500/6) 83.3
    12  50      (250/2) 125.0
    

    【讨论】:

      【解决方案4】:

      试试这个:

      SELECT ID , Salary,SUM(Salary) OVER(ORDER BY ID)Running Total
      FROM Your_Table
      

      更新答案:

      我花了一整天的时间才得到这个:

      DECLARE @TAB TABLE(ID INT, SALARY INT)
      
      INSERT INTO @TAB VALUES(1 ,100)
      INSERT INTO @TAB VALUES(2 , 40)
      INSERT INTO @TAB VALUES(3 , 30)
      INSERT INTO @TAB VALUES(4 , 40)
      INSERT INTO @TAB VALUES(5 , 90)
      INSERT INTO @TAB VALUES(6 ,160)
      INSERT INTO @TAB VALUES(7 , 70)
      INSERT INTO @TAB VALUES(8 , 40)
      INSERT INTO @TAB VALUES(9 , 20)
      INSERT INTO @TAB VALUES(10, 10)
      INSERT INTO @TAB VALUES(11,200)
      INSERT INTO @TAB VALUES(12, 50)
      
      
      DECLARE @MIN INT, @MAX INT, @PreVal INT, @CurVal INT, @OldVal INT,@NextVal,  INT, @ExistVal INT
      DECLARE @TABLE1 TABLE(ID INT, Sal INT, RunTotal INT)
      
      INSERT INTO @TABLE1 (Id, Sal)
      SELECT * FROM @TAB
      
      SELECT @MIN=MIN(ID),@MAX=MAX(ID) FROM @TAB
      
      WHILE(@MIN<=@MAX)
      BEGIN
          SELECT @NextVal=SALARY FROM @TAB WHERE ID=@MIN
          SELECT @ExistVal=SALARY FROM @TAB WHERE ID=@MIN-1
          SELECT @OldVal=RunTotal FROM @TABLE1 WHERE ID=@MIN-1
          SELECT @PreVal=RunTotal FROM @TABLE1 WHERE ID=@MIN-2
      
          IF(@OldVal>@PreVal+(@PreVal/2))
          BEGIN
              SELECT @CurVal = @NextVal+@ExistVal
          END
          ELSE
          BEGIN
              SELECT @CurVal=ISNULL(@OldVal,0)+@NextVal-- FROM @TABLE1 WHERE ID=@MIN
          END
      
          UPDATE @TABLE1 SET RunTotal=@CurVal WHERE ID=@MIN
          SELECT @MIN=@MIN+1
      END
      
      SELECT * FROM @TABLE1
      

      结果:

      ID  Sal RunTotal
      1   100 100
      2   40  140
      3   30  170
      4   40  210
      5   90  300
      6   160 460
      7   70  230
      8   40  270
      9   20  290
      10  10  300
      11  200 500
      12  50  250
      

      如果有人有更好的答案,请在 cmets 中提及。

      【讨论】:

      • 我刚刚意识到您如何计算运行总计值。通过除以第 6 行的值 2,您可以在第 7 行获得 RunTotal 值。(我的意思是这个 SELECT @CurVal = @CurVal/2)但我不希望这样,我想用第 6 行的薪水总和来获得价值和 7. 像“160 +70 = 230”。
      • 运行良好,再次感谢。你知道我在底部的意思吗? (移动平均线)
      猜你喜欢
      • 2013-08-01
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 2021-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-12
      相关资源
      最近更新 更多