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.