Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Може ли това рекурсивно решение да бъде записано в T-SQL заявка с помощта на CTE или OVER?

Обща сума. АКТУАЛИЗИРАНЕ на временна таблица спрямо 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;

Рекурсивно решение отнема 9 секунди:

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

АКТУАЛИЗАЦИЯ на таблицата отнема 0 секунди:

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;

Тези два подхода биха могли поне да ви дадат рамка, върху която да изградите заявката си.

Между другото в SQL Server, за разлика от MySQL, редът на присвояване на променливи няма значение. Това:

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

И следното:

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

И двете се изпълняват по един и същи начин, т.е. присвояването на променливи се извършва първо, независимо от позицията на присвояването на променливата в оператора. И двете заявки имат същия резултат:

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

На вашата точна таблица просто открийте Buy/Sell, можете или да го умножите съответно по 1 и -1, или просто да подпишете полетата, напр. :

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

Ако се случи да надстроите до SQL Server 2012, ето директната реализация на текущата обща сума:

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

За точния ви проблем:

select OrderID, Qty, 

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

from Test;

АКТУАЛИЗАЦИЯ

Ако се чувствате неспокойни с странна актуализация , можете да поставите предпазна клауза, за да проверите дали редът на редовете, които трябва да бъдат актуализирани, съвпада с оригиналния ред (подпомогнат от identity(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;

Ако UPDATE наистина актуализира редове в непредсказуем ред (или случайно ще го направи), @RN_Check вече няма да бъде равен на RN(identity order), кодът ще предизвика грешка при деление на нула тогава. Използвайки предпазна клауза, непредсказуемият ред на актуализиране ще откаже бързо ; ако това се случи тогава, ще е време да подадете бъг петиция до Microsoft да направи странната актуализация не толкова странна :-)

Хеджирането на предпазната клауза на присъщата императивна операция (присвояване на променлива) е наистина последователно.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как мога да вляза в съхранена процедура на SQL Server от моя C# код?

  2. DATEFROMPARTS() Примери в SQL Server (T-SQL)

  3. SQL Server - използване на CASE в клаузата WHERE

  4. Получаване на точните редактирани данни от SQL Server

  5. Как да изчислим възрастта (в години) въз основа на датата на раждане и getDate()