Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Valorisation des stocks basée sur FIFO dans SQL Server

Étonnamment difficile à obtenir correctement. Je soupçonne qu'il serait plus facile d'utiliser SQL Server 2012 qui prend en charge les sommes en cours d'exécution dans les fonctions de fenêtrage. Quoi qu'il en soit :

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 première observation est que nous n'avons rien à faire de spécial pour OUT transactions - nous avons juste besoin de connaître la quantité totale. C'est ce que le TotalOut CTE calcule. Les deux premiers CTE fonctionnent avec IN transactions et calculez quel "intervalle" de stock chacune représente - changez la requête finale en juste select * from RunningTotals pour avoir une idée de cela.

Le dernier SELECT recherche les lignes qui n'ont pas été complètement épuisées par les transactions sortantes, puis décide s'il s'agit de la quantité totale de cette transaction entrante, ou s'il s'agit de la transaction qui chevauche le total sortant.