Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Kann diese rekursive Lösung mit CTE oder OVER in eine T-SQL-Abfrage geschrieben werden?

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.