Lopend totaal. UPDATE tijdelijke tabel vs CTE
create table Test(
OrderID int primary key,
Qty int not null
);
declare @i int = 1;
while @i <= 5000 begin
insert into Test(OrderID, Qty) values (@i * 2,rand() * 10);
set @i = @i + 1;
end;
Recursieve oplossing duurt 9 seconden:
with T AS
(
select ROW_NUMBER() over(order by OrderID) as rn, * from test
)
,R(Rn, OrderId, Qty, RunningTotal) as
(
select Rn, OrderID, Qty, Qty
from t
where rn = 1
union all
select t.Rn, t.OrderId, t.Qty, p.RunningTotal + t.Qty
from t t
join r p on t.rn = p.rn + 1
)
select R.OrderId, R.Qty, R.RunningTotal from r
option(maxrecursion 0);
UPDATE-tabel duurt 0 seconden:
create function TestRunningTotal()
returns @ReturnTable table(
OrderId int, Qty int, RunningTotal int
)
as begin
insert into @ReturnTable(OrderID, Qty, RunningTotal)
select OrderID, Qty, 0 from Test
order by OrderID;
declare @RunningTotal int = 0;
update @ReturnTable set
RunningTotal = @RunningTotal,
@RunningTotal = @RunningTotal + Qty;
return;
end;
Die twee benaderingen zouden je op zijn minst een raamwerk kunnen bieden om je vraag op te bouwen.
Trouwens, in SQL Server, in tegenstelling tot MySQL, maakt de volgorde van variabele toewijzing niet uit. Dit:
update @ReturnTable set
RunningTotal = @RunningTotal,
@RunningTotal = @RunningTotal + Qty;
En het volgende:
update @ReturnTable set
@RunningTotal = @RunningTotal + Qty,
RunningTotal = @RunningTotal;
Ze worden beide op dezelfde manier uitgevoerd, d.w.z. de variabele toewijzingen gebeuren eerst, ongeacht de positie van de variabele toewijzing in de instructie. Beide zoekopdrachten hebben dezelfde uitvoer:
OrderId Qty RunningTotal
----------- ----------- ------------
2 4 4
4 8 12
6 4 16
8 5 21
10 3 24
12 8 32
14 2 34
16 9 43
18 1 44
20 2 46
22 0 46
24 2 48
26 6 54
Op uw exacte tafel, detecteer gewoon kopen/verkopen, u kunt het vermenigvuldigen met respectievelijk 1 en -1, of u ondertekent alleen de velden, b.v. :
update @ReturnTable set
@RunningTotal = @RunningTotal +
CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END,
RunningTotal = @RunningTotal;
Als u toevallig upgradet naar SQL Server 2012, volgt hier de eenvoudige implementatie van running total:
select OrderID, Qty, sum(Qty) over(order by OrderID) as RunningTotal
from Test
Over uw exacte probleem:
select OrderID, Qty,
sum(CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END)
over(order by OrderID) as RunningTotal
from Test;
UPDATE
Als je je ongemakkelijk voelt bij eigenzinnige update , kunt u een bewakingsclausule plaatsen om te controleren of de volgorde van de rijen die moeten worden bijgewerkt, overeenkomt met de oorspronkelijke volgorde (met behulp van identiteit (1,1)):
create function TestRunningTotalGuarded()
returns @ReturnTable table(
OrderId int, Qty int,
RunningTotal int not null,
RN int identity(1,1) not null
)
as begin
insert into @ReturnTable(OrderID, Qty, RunningTotal)
select OrderID, Qty, 0 from Test
order by OrderID;
declare @RunningTotal int = 0;
declare @RN_check INT = 0;
update @ReturnTable set
@RN_check = @RN_check + 1,
@RunningTotal =
(case when RN = @RN_check then @RunningTotal + Qty else 1/0 end),
RunningTotal = @RunningTotal;
return;
end;
Als UPDATE echt rijen in onvoorspelbare volgorde bijwerkt (of dat toevallig zal doen), zal de @RN_Check niet meer gelijk zijn aan RN (identiteitsvolgorde), de code zal een delig-door-nul-fout veroorzaken dan. Met behulp van de bewakingsclausule zal een onvoorspelbare update-bestelling snel mislukken
; als dit gebeurt, is het tijd om een bug in te dienen petitie aan Microsoft om de eigenzinnige update niet zo eigenzinnig te maken :-)
De beschermingsclausule hedge op de inherent imperatieve operatie (variabele toewijzing) is echt sequentieel.