我有一个存储类似银行对帐单的交易的表。例如:
ID |Date |Amount |Balance |
--------------------------------------
1 |01/01/2018 | 100.00| 100.00|
2 |01/02/2018 | 50.00| 150.00|
3 |01/04/2018 | -5.00| 145.00|
4 |01/05/2018 | 10.00| 155.00|
在理想的世界中,交易将按时间顺序插入,因此最后一笔交易之前的余额不应该更新,但事实并非如此。我们假设我们添加了一个日期为01/03/2018的交易,后续余额应该更新,结果表应该是:
ID |Date |Amount |Balance |
--------------------------------------
1 |01/01/2018 | 100.00| 100.00|
2 |01/02/2018 | 50.00| 150.00|
3 |01/04/2018 | -5.00| 165.00|
4 |01/05/2018 | 10.00| 175.00|
5 |01/03/2018 | 20.00| 170.00|
但按日期排序应该如下:
ID |Date |Amount |Balance |
--------------------------------------
1 |01/01/2018 | 100.00| 100.00|
2 |01/02/2018 | 50.00| 150.00|
5 |01/03/2018 | 20.00| 170.00|
3 |01/04/2018 | -5.00| 165.00|
4 |01/05/2018 | 10.00| 175.00|
事务可以随任何日期进入,甚至在表中最旧的事务之前,代码应该能够在有序或无序(按日期字段)表中插入行。
我该怎么做?提前谢谢!
答案 0 :(得分:2)
假设表中的现有余额已处于正确状态。
我尝试在SQL更新语句中使用变量解决此问题。 假设我们有初始数据 -
declare @xyz table (ID int,[Date] date,Amount decimal(10,2),Balance decimal(10,2))
insert into @xyz (ID, [Date], Amount, Balance)
select 1 ,'01/01/2018', 100.00, 100.00 union all
select 2 ,'01/02/2018', 50.00, 150.00 union all
select 3 ,'01/04/2018', -5.00, 145.00 union all
select 4 ,'01/05/2018', 10.00, 155.00 union all
select 5 ,'01/05/2018', 30.00, 185.00 union all
select 6 ,'01/07/2018', 25.00, 250.00 union all
select 7 ,'01/06/2018', 40.00, 225.00
现在,应用程序向我们发送带有这些值的新插入 -
declare
@date date = '12/08/2017',
@amount decimal(10,2) = 20.0
以上新事务值发送到过程和程序内部的代码如下所示 -
declare @maxDate date = (select max([date]) from @xyz)
declare @balance decimal(10,2) =
(select top 1 Balance + @amount from @xyz where [Date] < @date order by [date] desc,ID desc)
insert into @xyz (ID, Date, Amount, Balance)
select 8, @date, @amount, isnull(@balance,@amount)
if @maxDate > @date
update x
set Balance = Balance + @amount
from @xyz as x
where Date > @date
这对我有用,但如果不正确,请告诉我。
答案 1 :(得分:1)
Bhatia Ashish的答案确实是一个非常巧妙的答案,但它错过了一个关键点。也就是说,如果一次添加多个新行,它会中断。我重新使用了这种方法并使其完全具有设置意识。享受:
-- Synched data
declare @t table (
Id int identity(1,1) primary key,
CreateDate date not null,
Amount money not null,
Balance money not null
);
-- New rows
declare @newdata table (
CreateDate date not null,
Amount money not null,
-- Running total of balance changes
RT money null,
-- Previous balance value from original data
PrevBalance money null
);
-- Original data, correct balances
insert into @t (CreateDate, Amount, Balance)
values
('20180101', $100, $100),
('20180102', $50, $150),
('20180104', $-5, $145),
('20180105', $10, $155),
('20180108', $24, $179),
('20180110', $-17, $162),
('20180111', $-11, $151);
-- DEBUG: Before sync
select * from @t t order by t.CreateDate;
-- New rows
insert into @newdata (CreateDate, Amount)
values
('20171227', $41),
('20180103', $20),
('20180106', $-100),
('20180107', $36),
('20180109', $29);
-- Materialise window functions for the update source
with cte as (
select n.CreateDate,
sum(n.Amount) over(order by n.CreateDate) as [RT]
from @newdata n
)
update n set RT = c.RT, PrevBalance = isnull(ca.Balance, $0)
from @newdata n
inner join cte c on c.CreateDate = n.CreateDate
outer apply (
select top (1) t.Balance from @t t
where t.CreateDate < n.CreateDate
order by t.CreateDate desc
) ca;
-- DEBUG: New transactions with supplementary data
select * from @newdata;
-- Put new rows into main table
insert into @t (CreateDate, Amount, Balance)
select n.CreateDate, n.Amount, $0
from @newdata n;
-- Correct balances for all transactions affected
update t set Balance = ca.RT
+ case
when t.CreateDate = ca.CreateDate then ca.PrevBalance
else t.Balance
end
from @t t
cross apply (
select top (1) n.CreateDate, n.RT, n.PrevBalance
from @newdata n where n.CreateDate <= t.CreateDate
order by n.CreateDate desc
) ca;
-- DEBUG: After sync
select * from @t t order by t.CreateDate;
编辑:更正了其中一个插入的交易发生的时间早于任何现有交易。