Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Czy to rozwiązanie rekurencyjne można zapisać w zapytaniu T-SQL przy użyciu CTE lub OVER?

Suma bieżąca. AKTUALIZUJ tabelę tymczasową a 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;

Rozwiązanie rekurencyjne trwa 9 sekund:

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

Aktualizuj tabelę trwa 0 sekund:

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;

Te dwa podejścia mogą przynajmniej zapewnić ramy, na których można zbudować zapytanie.

BTW w SQL Server, w przeciwieństwie do MySQL, kolejność przypisywania zmiennych nie ma znaczenia. To:

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

Oraz następujące:

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

Obydwa działają w ten sam sposób, tzn. przypisania zmiennych następują jako pierwsze, niezależnie od pozycji przypisania zmiennej w instrukcji. Oba zapytania mają te same wyniki:

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

Na swojej dokładnej tabeli po prostu wykryj Buy/Sell, możesz albo pomnożyć ją odpowiednio przez 1 i -1, albo po prostu podpisać pola, np. :

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

Jeśli zdarzy ci się uaktualnić do SQL Server 2012, oto prosta implementacja sumy bieżącej:

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

Dokładny problem:

select OrderID, Qty, 

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

from Test;

AKTUALIZUJ

Jeśli czujesz się nieswojo z dziwaczna aktualizacja , możesz umieścić klauzulę ochronną, aby sprawdzić, czy kolejność wierszy, które mają zostać zaktualizowane, jest zgodna z oryginalną kolejnością (wspomagana tożsamością (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;

Jeśli UPDATE naprawdę zaktualizuje wiersze w nieprzewidywalnej kolejności (lub przypadkiem tak się stanie), @RN_Check nie będzie już równy RN(kolejność tożsamości), kod zgłosi błąd dzielenia przez zero następnie. Używając klauzuli ochronnej, nieprzewidywalna kolejność aktualizacji szybko się nie powiedzie ; jeśli tak się stanie, nadejdzie czas, aby zgłosić błąd petycja do Microsoftu, aby ta dziwaczna aktualizacja nie była tak dziwaczna :-)

Zabezpieczenie klauzuli ochronnej na z natury koniecznej operacji (przypisanie zmiennej) jest naprawdę sekwencyjne.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. BULK INSERT z niespójną liczbą kolumn

  2. Czy szybciej jest UAKTUALNIĆ wiersz, czy USUNĄĆ go i WSTAWIĆ nowy?

  3. Problemy z porównaniem SQL i wartościami null

  4. Odniesienie Procedury składowane dla wszystkich baz danych na serwerze

  5. Jak sp_describe_first_result_set działa w programie SQL Server