sql >> Database teknologi >  >> RDS >> Sqlserver

Kan denne rekursive løsning skrives op i en T-SQL-forespørgsel ved hjælp af CTE eller OVER?

I alt. OPDATERING temp 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;

Rekursiv løsning tager 9 sekunder:

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);

OPDATERING tabel tager 0 sekunder:

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;

Disse to tilgange kunne i det mindste give dig en ramme at bygge din forespørgsel på.

BTW i SQL Server, i modsætning til i MySQL, betyder rækkefølgen af ​​variabel tildeling ikke noget. Dette:

update @ReturnTable set 
    RunningTotal = @RunningTotal, 
    @RunningTotal = @RunningTotal + Qty;

Og følgende:

update @ReturnTable set 
    @RunningTotal = @RunningTotal + Qty,
    RunningTotal = @RunningTotal; 

De udfører begge på samme måde, dvs. variabeltildelingerne sker først, uanset variabeltildelingens placering i sætningen. Begge forespørgsler har samme output:

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

På din præcise tabel skal du blot registrere Køb/Sælg, du kan enten gange det med henholdsvis 1 og -1, eller du underskriver blot felterne, f.eks. :

update @ReturnTable set 
       @RunningTotal = @RunningTotal + 
                       CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END,
       RunningTotal = @RunningTotal;            

Hvis du tilfældigvis opgraderer til SQL Server 2012, er her den enkle implementering af running total:

select OrderID, Qty, sum(Qty) over(order by OrderID) as RunningTotal
from Test

Om dit præcise problem:

select OrderID, Qty, 

   sum(CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END) 
   over(order by OrderID) as RunningTotal

from Test;

OPDATERING

Hvis du føler dig utilpas med finurlig opdatering , du kan indsætte en guard-klausul for at kontrollere, om rækkefølgen af ​​rækker, der skal opdateres, stemmer overens med den oprindelige rækkefølge(støttet af identitet(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;

Hvis OPDATERING virkelig opdaterer rækker i uforudsigelig rækkefølge (eller tilfældigvis gør det det), vil @RN_Check ikke længere være lig med RN(identitetsrækkefølge), koden vil give en divide-by-nul fejl derefter. Ved at bruge guard-klausul vil uforudsigelig opdateringsrækkefølge mislykkes hurtigt ; hvis dette sker, vil det være tid til at indgive en fejl andragende til Microsoft om at gøre den finurlige opdatering ikke så finurlig :-)

Beskyttelsesklausulen afdækning af den iboende imperative operation (variabel tildeling) er virkelig sekventiel.



  1. Oprettelse af et multidimensionelt array ud fra forespørgsel

  2. Mysql inkonsekvent antal rækker count(*) vs table.table_rows i informationsskema

  3. mysql:bruge SET eller mange kolonner?

  4. MySQL:OPDATERE tabel med COUNT fra en anden tabel?