逐行计算总计 运行 的查询
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 的所有帮助。
我有一个问题,工作在一周开始时变为 '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 的所有帮助。