Tổng số đang chạy. CẬP NHẬT bảng tạm thời so với 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;
Giải pháp đệ quy mất 9 giây:
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);
Bảng CẬP NHẬT mất 0 giây:
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;
Hai cách tiếp cận đó ít nhất có thể cung cấp cho bạn một khuôn khổ để xây dựng truy vấn của bạn.
BTW trong SQL Server, không giống như trong MySQL, thứ tự gán biến không quan trọng. Cái này:
update @ReturnTable set
RunningTotal = @RunningTotal,
@RunningTotal = @RunningTotal + Qty;
Và những điều sau:
update @ReturnTable set
@RunningTotal = @RunningTotal + Qty,
RunningTotal = @RunningTotal;
Cả hai đều thực thi theo cùng một cách, tức là các phép gán biến xảy ra đầu tiên, bất kể vị trí của phép gán biến trong câu lệnh. Cả hai truy vấn đều có cùng đầu ra:
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
Trên bảng chính xác của bạn, chỉ cần phát hiện Mua / Bán, bạn có thể nhân nó với 1 và -1 tương ứng hoặc bạn chỉ cần ký tên vào các trường, ví dụ::
update @ReturnTable set
@RunningTotal = @RunningTotal +
CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END,
RunningTotal = @RunningTotal;
Nếu bạn tình cờ nâng cấp lên SQL Server 2012, đây là cách triển khai đơn giản của tổng số đang chạy:
select OrderID, Qty, sum(Qty) over(order by OrderID) as RunningTotal
from Test
Về vấn đề chính xác của bạn:
select OrderID, Qty,
sum(CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END)
over(order by OrderID) as RunningTotal
from Test;
CẬP NHẬT
Nếu bạn cảm thấy không ổn với cập nhật kỳ quặc , bạn có thể đặt một điều khoản bảo vệ để kiểm tra xem thứ tự của các hàng sẽ được cập nhật có khớp với thứ tự ban đầu hay không (được hỗ trợ bởi danh tính (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;
Nếu UPDATE thực sự cập nhật các hàng theo thứ tự không thể đoán trước (hoặc bất kỳ trường hợp nào xảy ra), @RN_Check sẽ không bằng RN (thứ tự nhận dạng) nữa, mã sẽ gây ra lỗi chia cho không sau đó. Sử dụng điều khoản bảo vệ, thứ tự cập nhật không thể đoán trước sẽ không nhanh
; nếu điều này xảy ra sau đó, đây sẽ là lúc để gửi một bug kiến nghị với Microsoft để làm cho bản cập nhật kỳ quặc không kỳ quặc như vậy :-)
Mệnh đề bảo vệ hàng rào đối với hoạt động mệnh lệnh vốn có (gán biến) thực sự là tuần tự.