使用上一行值 SQL 服务器递归计算当前行的总值
Recursively calculate total value of current row using previous row value SQL Server
假设我有一个这样的table
Id Initial Value CardCount Total
1 5 1 null
1 0 2 null
1 0 4 null
2 10 0 null
2 0 3 null
2 0 1 null
并且我想将总计列更新为前一行的 "total" 值 + 当前行的 cardCount 的总和。对于每个 Id 中的第一条记录,总数是 InitialValue + CardCount。我希望结果是这样的:
Id Initial Value CardCount Total
1 5 1 6
1 0 2 8
1 0 4 12
2 10 0 10
2 0 3 13
2 0 1 14
我试过了,但结果不正确
declare @tmp Table(Id int, InitialValue int, CardCount int, total int null)
Insert into @tmp values (1, 5, 1, null)
Insert into @tmp values (1, 0, 2, null)
Insert into @tmp values (1, 0, 4, null)
Insert into @tmp values (2, 10, 0, null)
Insert into @tmp values (2, 0, 3, null)
Insert into @tmp values (2, 0, 1, null)
Update @tmp Set Total = InitialValue + CardCount
;with TmpTb as(
select
ROW_NUMBER() over (order by ID) RNum, *
From
@tmp)
update c set Total= x.Total + CardCount
from TmpTb c join
(select a.RNum, a.ID, b.Total
From TmpTb a LEFT JOIN TmpTb b on a.RNum=b.RNum+1
where a.ID=b.ID )x on c.RNum>=x.RNum AND c.ID=x.ID
select * from @tmp
如果您使用的是 SQL 服务器的现代版本,它可以像 window 函数一样进行聚合,则不需要为此使用递归。然后你可以做 sum()
作为 window 函数:
update t1 set total = y.total
from (select *, rn = ROW_NUMBER() over (order by id) from @tmp) t1
join (
select id, rn, sum(initialvalue+cardcount) over (partition by id order by rn) as total
from (select *, rn = ROW_NUMBER() over (order by id) from @tmp) x
) y
on t1.rn = y.rn
使用您的示例数据,结果将是:
id initialvalue CardCount total
1 5 1 6
1 0 2 8
1 0 4 12
2 10 0 10
2 0 3 13
2 0 1 14
这可能有效,也可能无效。此代码依赖于服务器每次都以相同的顺序返回行,这不是您可以依赖的,但是由于您的数据似乎没有任何东西可以确定顺序,这可能是您可以期望的最好的顺序。如果你想产生一个 运行 总数,你确实需要一个稳定的属性来以确定的方式对数据进行排序。
重点是,如果你不能对数据进行排序,你就无法确定地实现你想要的。
假设我有一个这样的table
Id Initial Value CardCount Total
1 5 1 null
1 0 2 null
1 0 4 null
2 10 0 null
2 0 3 null
2 0 1 null
并且我想将总计列更新为前一行的 "total" 值 + 当前行的 cardCount 的总和。对于每个 Id 中的第一条记录,总数是 InitialValue + CardCount。我希望结果是这样的:
Id Initial Value CardCount Total
1 5 1 6
1 0 2 8
1 0 4 12
2 10 0 10
2 0 3 13
2 0 1 14
我试过了,但结果不正确
declare @tmp Table(Id int, InitialValue int, CardCount int, total int null)
Insert into @tmp values (1, 5, 1, null)
Insert into @tmp values (1, 0, 2, null)
Insert into @tmp values (1, 0, 4, null)
Insert into @tmp values (2, 10, 0, null)
Insert into @tmp values (2, 0, 3, null)
Insert into @tmp values (2, 0, 1, null)
Update @tmp Set Total = InitialValue + CardCount
;with TmpTb as(
select
ROW_NUMBER() over (order by ID) RNum, *
From
@tmp)
update c set Total= x.Total + CardCount
from TmpTb c join
(select a.RNum, a.ID, b.Total
From TmpTb a LEFT JOIN TmpTb b on a.RNum=b.RNum+1
where a.ID=b.ID )x on c.RNum>=x.RNum AND c.ID=x.ID
select * from @tmp
如果您使用的是 SQL 服务器的现代版本,它可以像 window 函数一样进行聚合,则不需要为此使用递归。然后你可以做 sum()
作为 window 函数:
update t1 set total = y.total
from (select *, rn = ROW_NUMBER() over (order by id) from @tmp) t1
join (
select id, rn, sum(initialvalue+cardcount) over (partition by id order by rn) as total
from (select *, rn = ROW_NUMBER() over (order by id) from @tmp) x
) y
on t1.rn = y.rn
使用您的示例数据,结果将是:
id initialvalue CardCount total
1 5 1 6
1 0 2 8
1 0 4 12
2 10 0 10
2 0 3 13
2 0 1 14
这可能有效,也可能无效。此代码依赖于服务器每次都以相同的顺序返回行,这不是您可以依赖的,但是由于您的数据似乎没有任何东西可以确定顺序,这可能是您可以期望的最好的顺序。如果你想产生一个 运行 总数,你确实需要一个稳定的属性来以确定的方式对数据进行排序。
重点是,如果你不能对数据进行排序,你就无法确定地实现你想要的。