Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Valutazione dell'inventario delle scorte basata su FIFO in SQL Server

Sorprendentemente difficile da ottenere. Sospetto che sarebbe più semplice usare SQL Server 2012 che supporta l'esecuzione di somme nelle funzioni di windowing. Comunque:

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

La prima osservazione è che non abbiamo bisogno di fare niente di speciale per OUT transazioni - abbiamo solo bisogno di conoscere la quantità totale. Ecco cosa è il TotalOut CTE calcola. I primi due CTE funzionano con IN transazioni e calcola quale "intervallo" di azioni rappresenta ciascuno - cambia la query finale semplicemente in select * from RunningTotals per avere un'idea di questo.

Il SELECT finale trova le righe che non sono state completamente esaurite dalle transazioni in uscita, quindi decide se si tratta dell'intera quantità di quella transazione in entrata o se si tratta della transazione a cavallo del totale in uscita.