Изненадващо трудно да се направи правилно. Подозирам, че би било по-лесно с помощта на SQL Server 2012, който поддържа текущи суми в прозоречни функции. Както и да е:
declare @Stock table (Item char(3) not null,[Date] datetime not null,TxnType varchar(3) not null,Qty int not null,Price decimal(10,2) null)
insert into @Stock(Item , [Date] , TxnType, Qty, Price) values
('ABC','20120401','IN', 200, 750.00),
('ABC','20120405','OUT', 100 ,null ),
('ABC','20120410','IN', 50, 700.00),
('ABC','20120416','IN', 75, 800.00),
('ABC','20120425','OUT', 175, null ),
('XYZ','20120402','IN', 150, 350.00),
('XYZ','20120408','OUT', 120 ,null ),
('XYZ','20120412','OUT', 10 ,null ),
('XYZ','20120424','IN', 90, 340.00);
;WITH OrderedIn as (
select *,ROW_NUMBER() OVER (PARTITION BY Item ORDER BY [DATE]) as rn
from @Stock
where TxnType = 'IN'
), RunningTotals as (
select Item,Qty,Price,Qty as Total,0 as PrevTotal,rn from OrderedIn where rn = 1
union all
select rt.Item,oi.Qty,oi.Price,rt.Total + oi.Qty,rt.Total,oi.rn
from
RunningTotals rt
inner join
OrderedIn oi
on
rt.Item = oi.Item and
rt.rn = oi.rn - 1
), TotalOut as (
select Item,SUM(Qty) as Qty from @Stock where TxnType='OUT' group by Item
)
select
rt.Item,SUM(CASE WHEN PrevTotal > out.Qty THEN rt.Qty ELSE rt.Total - out.Qty END * Price)
from
RunningTotals rt
inner join
TotalOut out
on
rt.Item = out.Item
where
rt.Total > out.Qty
group by rt.Item
Първото наблюдение е, че не е нужно да правим нищо специално за OUT
транзакции - просто трябва да знаем общото количество. Това е, което TotalOut
CTE изчислява. Първите два CTE работят с IN
транзакции и изчислете какъв „интервал“ на складовите наличности представлява всеки - променете крайната заявка просто на select * from RunningTotals
за да усетите това.
Последният SELECT
оператор намира редове, които не са били напълно изчерпани от изходящите транзакции, и след това решава дали това е цялото количество на тази входяща транзакция, или това е транзакцията, която обхваща изходящата обща сума.