Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

FIFO-basierte Bestandsbewertung in SQL Server

Überraschend schwer richtig zu machen. Ich vermute, es wäre einfacher, SQL Server 2012 zu verwenden, der laufende Summen in Fensterfunktionen unterstützt. Wie auch immer:

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

Die erste Beobachtung ist, dass wir für OUT nichts Besonderes tun müssen Transaktionen - wir müssen nur die Gesamtmenge kennen. Dafür steht der TotalOut CTE berechnet. Die ersten beiden CTEs arbeiten mit IN Transaktionen und berechnen Sie, welches "Intervall" der Aktien jeweils dargestellt wird - ändern Sie die letzte Abfrage so, dass sie einfach select * from RunningTotals ist um ein Gefühl dafür zu bekommen.

Das letzte SELECT -Anweisung findet Zeilen, die nicht vollständig durch ausgehende Transaktionen erschöpft sind, und entscheidet dann, ob es sich um die gesamte Menge dieser eingehenden Transaktion handelt oder ob es sich um die Transaktion handelt, die die ausgehende Gesamtsumme überspannt.