sql >> Base de Datos >  >> RDS >> Sqlserver

¿Se puede escribir esta solución recursiva en una consulta T-SQL usando CTE o OVER?

Total acumulado. ACTUALIZAR tabla temporal 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;

Solución recursiva toma 9 segundos:

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

tabla ACTUALIZAR toma 0 segundo:

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;

Esos dos enfoques podrían al menos brindarle un marco para construir su consulta.

Por cierto, en SQL Server, a diferencia de MySQL, el orden de asignación de variables no importa. esto:

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

Y lo siguiente:

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

Ambos se ejecutan de la misma manera, es decir, las asignaciones de variables ocurren primero, independientemente de la posición de la asignación de variables en la instrucción. Ambas consultas tienen el mismo resultado:

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

En su tabla exacta, solo detecte Compra/Venta, puede multiplicarla por 1 y -1 respectivamente, o simplemente firmar los campos, p. :

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

Si actualiza a SQL Server 2012, esta es la implementación directa del total acumulado:

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

Sobre su problema exacto:

select OrderID, Qty, 

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

from Test;

ACTUALIZAR

Si se siente incómodo con actualización peculiar , puede colocar una cláusula de protección para verificar si el orden de las filas que se actualizarán coincide con el orden original (con la ayuda de la identidad (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;

Si UPDATE realmente actualiza las filas en un orden impredecible (o por casualidad lo hará), @RN_Check ya no será igual a RN (orden de identidad), el código generará un error de división por cero después. Usando la cláusula de protección, el orden de actualización impredecible fallará rápidamente ; si esto sucede, será el momento de presentar un error petición a Microsoft para que la peculiar actualización no sea tan peculiar :-)

La cobertura de la cláusula de protección en la operación inherentemente imperativa (asignación de variables) es realmente secuencial.