Laufende Summe. UPDATE Temp-Tabelle 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;
Rekursive Lösung dauert 9 Sekunden:
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-Tabelle dauert 0 Sekunden:
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;
Diese beiden Ansätze könnten Ihnen zumindest einen Rahmen bieten, auf dem Sie Ihre Abfrage aufbauen können.
Übrigens spielt in SQL Server im Gegensatz zu MySQL die Reihenfolge der Variablenzuweisung keine Rolle. Dies:
update @ReturnTable set
RunningTotal = @RunningTotal,
@RunningTotal = @RunningTotal + Qty;
Und das Folgende:
update @ReturnTable set
@RunningTotal = @RunningTotal + Qty,
RunningTotal = @RunningTotal;
Beide werden auf die gleiche Weise ausgeführt, d. h. die Variablenzuweisungen erfolgen zuerst, unabhängig von der Position der Variablenzuweisung in der Anweisung. Beide Abfragen haben dieselbe Ausgabe:
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
Auf Ihrer genauen Tabelle finden Sie einfach Buy/Sell, Sie können es entweder mit 1 bzw. -1 multiplizieren oder Sie signieren nur die Felder, z. :
update @ReturnTable set
@RunningTotal = @RunningTotal +
CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END,
RunningTotal = @RunningTotal;
Wenn Sie zufällig auf SQL Server 2012 aktualisieren, finden Sie hier die einfache Implementierung von Running Total:
select OrderID, Qty, sum(Qty) over(order by OrderID) as RunningTotal
from Test
Zu deinem genauen Problem:
select OrderID, Qty,
sum(CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END)
over(order by OrderID) as RunningTotal
from Test;
AKTUALISIEREN
Wenn Sie sich mit unwohl fühlen skurriles Update , können Sie eine Schutzklausel einfügen, um zu überprüfen, ob die Reihenfolge der zu aktualisierenden Zeilen mit der ursprünglichen Reihenfolge übereinstimmt (unterstützt durch Identität (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;
Wenn UPDATE Zeilen wirklich in unvorhersehbarer Reihenfolge aktualisiert (oder zufällig wird), wird @RN_Check nicht mehr gleich RN(Identitätsreihenfolge) sein, der Code wird einen Teile-durch-Null-Fehler auslösen dann. Bei Verwendung einer Schutzklausel wird eine unvorhersehbare Update-Reihenfolge schnell fehlschlagen
; Wenn dies dann passiert, ist es an der Zeit, einen Bug zu melden Petition an Microsoft, das skurrile Update nicht so skurril zu machen :-)
Die Schutzklausel schützt die inhärent zwingende Operation (Variablenzuweisung) wirklich sequentiell.