在 SQL Server 中为滚动总和设置非负底限

Rya*_*yan 5 sql-server-2008 sql-server running-totals

我需要在滚动总和计算上设置一个下限。例如,与

PKID    NumValue    GroupID
----------------------------
1       -1          1
2       -2          1
3       5           1
4       -7          1
5       1           2
Run Code Online (Sandbox Code Playgroud)

我想拥有:

PKID   RollingSum    GroupID
----------------------------- ## Explanation: 
1      0             1        ## 0 - 1 < 0  => 0
2      0             1        ## 0 - 2 < 0  => 0
3      5             1        ## 0 + 5 > 0  => 5
4      0             1        ## 5 - 7 < 0  => 0
Run Code Online (Sandbox Code Playgroud)

当添加一个负数将导致总和为负时,将激活限制以将结果设置为零。后续的加法应该基于这个调整后的值,而不是原来的滚动总和。

应该使用加法来达到预期的结果。如果第四个数字从 -7 变为 -3,则第四个结果应该是 2 而不是 0

如果可以提供单个金额而不是几个滚动数字,那也是可以接受的。我可以使用存储过程来实现非负加法,但这太低级了。

现实生活中的问题是我们将下订单记录为正数,取消订单为负数。由于连接问题,客户可能cancel会多次单击该按钮,这将导致记录多个负值。在计算我们的收入时,“零”需要作为销售额的界限。


这个业务应用程序绝对是愚蠢的,但我对此无能为力。对于这个问题,请只考虑 DBA 可以使用的解决方案。

我希望每个GroupID最多五十行。

Sco*_*red 3

这是我想出的一个递归 CTE 示例(似乎有效)。使用 Row_Number() OVER 创建一个没有间隙的序列号。我不知道它对您的数据的表现如何,但值得尝试。

--Set up demo data
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
    drop table #temp
go
create table #temp (PKID int, NumValue int, GroupID int)
insert into #temp values
(1,-1,1), (3,-2,1), (5,5,1), (7,-3,1), (9,1,2)

--here is the real code
;
with RowNumberAddedToTemp as 
(
SELECT 
  ROW_NUMBER() OVER(ORDER BY PKID ASC) AS rn,
  * from #temp
  )
,x
AS (
    SELECT PKID         --Anchor row
        ,NumValue
        ,RunningTotal = CASE 
            WHEN NumValue < 0   --if initial value less than zero, make zero
                THEN 0
            ELSE NumValue
            END
        ,GroupID
        ,rn
    FROM RowNumberAddedToTemp
    WHERE rn = 1      

    UNION ALL

    SELECT y.PKID
        ,y.NumValue
        ,CASE 
            WHEN x.GroupID <> y.groupid     --did GroupId change?
                THEN CASE 
                        WHEN y.NumValue < 0     --if value is less than zero, make zero
                            THEN 0
                        ELSE y.numvalue         --start new groupid totals
                        END
            WHEN x.RunningTotal + y.NumValue < 0    --If adding the current row makes the total < 0, make zero
                THEN 0
            ELSE x.RunningTotal + y.NumValue        --Add to the running total for the current groupid
            END
        ,y.Groupid
        ,y.rn
    FROM x
    INNER JOIN RowNumberAddedToTemp AS y ON y.rn = x.rn + 1
    )
SELECT PKID
    ,Numvalue
    ,RunningTotal
    ,GroupID
FROM x
ORDER BY PKID
OPTION (MAXRECURSION 10000);
Run Code Online (Sandbox Code Playgroud)