逐行计算总计 运行 的查询

Query with row by row calculation for running total

我有一个问题,工作在一周开始时变为 'due',并且每周都有一定数量的 'slots' 可用于完成任何未完成的工作。如果没有足够的空位,那么工作将滚动到下周。

我的初始 table 看起来像这样:

Week Slots Due
23/8/2021 0 1
30/8/2021 2 3
6/9/2021 5 2
13/9/2021 1 4

我想在每周结束时维持 运行 总数的 'due' 个作业。 每周到期的人数将添加到上周的 运行 总数中,然后减去本周的名额数量。如果有足够的插槽来完成所有需要的工作,那么 运行 总数将为 0(从不为负)。

举个例子——下面显示了我将如何在 javascript 中实现这一点:

var Total = 0;
data.foreach(function(d){
    Total += d.Due;
    Total -= d.Slots;
    Total = Total > 0 ? Total : 0;
    d.Total = Total;
});

结果如下:

Week Slots Due Total
23/8/2021 0 1 1
30/8/2021 2 3 2
6/9/2021 5 2 0
13/9/2021 1 4 3

我可以在SQL(特别是SQL Server 2012)

中实现这个吗

我试过各种形式sum(xxx) over (order by yyy)

我最接近的是:

sum(Due) over (order by Week) - sum(Slots) over (order by Week) as Total

这提供了 运行 总计,但当有多余的插槽时将提供负总计。

这是使用游标执行此操作的唯一方法吗?如果是这样 - 有什么建议吗?

谢谢。

根据评论中的建议对我自己的问题的可能答案。

Thorsten Kettner 建议递归查询:

with cte as (

select [Week], [Due], [Slots]
,case when Due > Slots then Due - Slots else 0 end as [Total]
from [Data]
where [Week] = (select top 1 [Week] from [Data])

union all

select  e.[Week], e.[Due], e.[Slots]
, case when cte.Total + e.Due - e.Slots > 0 then cte.Total + e.Due - e.Slots else 0 end as [Total]
from [Data] e
inner join cte on cte.[Week] = dateadd(day,-7,e.[Week])
)

select * from cte

OPTION (MAXRECURSION 200)

Thorsten - 这是您的建议吗? (如果您有任何改进,请post作为答案,以便我接受!)

大概我必须确保 MAXRECURSION 设置为高于我将处理的行数?

我对 dateadd(day,-7,e.[Week]) 上的加入有点紧张。我会更好地使用 Row_Number() 来获取以前的记录吗?我可能想使用周以外的东西,或者周可能会丢失?

George Menoutis 建议了一个 'while' 查询,当我遇到这个 post 时,我正在寻找实现它的方法:

这表明与一段时间相比,游标可能并没有那么糟糕?

这是我想出的基于游标的版本:

SET NOCOUNT ON;
DECLARE @Week Date,
        @Due Int,
        @Slots Int,
        @Total Int = 0;

DECLARE @Output TABLE ([Week] Date NOT NULL, Due Int NOT NULL, Slots Int NOT NULL, Total Int);

DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR  SELECT [Week], Due, Slots
     FROM   [Data]
    ORDER BY [Week] ASC;

OPEN crs;

FETCH NEXT
FROM  crs
INTO  @Week, @Due, @Slots;

WHILE (@@FETCH_STATUS = 0)
BEGIN
    Set @Total = @Total + @Due;
    Set @Total = @Total - @Slots;
    Set @Total = IIF(@Total > 0, @Total , 0)

    INSERT INTO @Output ([Week], [Due], [Slots], [Total])
    VALUES (@Week, @Due, @Slots, @Total);

    FETCH NEXT
    FROM  crs
    INTO  @Week, @Due, @Slots;
END;

CLOSE crs;
DEALLOCATE crs;

SELECT *
FROM   @Output;

这两个似乎都按预期工作。递归查询 感觉 更好(cursors = bad 等),但它是否设计为以这种方式使用(每个输入行都有一个递归,因此可能有非常多的递归?)

非常感谢大家的意见:-)

根据 Thorsten

对先前答案的改进
with numbered as (
select *, ROW_NUMBER() OVER (ORDER BY [Week]) as RN
from [Data]
)
,cte as (
select [Week], [Due], [Slots], [RN]
,case when Due > Slots then Due - Slots else 0 end as [Total]
from numbered
where RN = 1

union all

select  e.[Week], e.[Due], e.[Slots], e.[RN]
, case when cte.Total + e.Due - e.Slots > 0 then cte.Total + e.Due - e.Slots else 0 end as [Total]
from numbered e
inner join cte on cte.[RN] = e.[RN] - 1
)

select * from cte

OPTION (MAXRECURSION 0)

非常感谢 Thorsten 的所有帮助。