'Calculating Cost of Sales in SQL using FIFO method
From a stock transaction table I've created following sorted view table to be ready for FIFO:
| rowN | date_ | stockCode | sign_ | amount | unitPrice |
|---|---|---|---|---|---|
| 1 | '2022-01-20' | ABC | in | 5 | 29.20 |
| 2 | '2022-01-22' | ABC | in | 3 | 32.50 |
| 3 | '2022-01-23' | ABC | out | 7 | 40.00 |
| 4 | '2022-01-23' | ABC | out | 1 | 42.00 |
| 5 | '2022-01-01' | XYZ | in | 3 | 20.50 |
| 6 | '2022-01-03' | XYZ | out | 3 | 25.00 |
and I want to create a select query which looks like the previous table with only "out" rows and added cost_of_sales columns which is FIFO cost of that sale. But my knowledge of SQL is limited by just joins and sum over partitions.
The resulting table in my mind should look like this:
| rowN | date_ | stockCode | sign_ | amount | unitPrice | cost_of_sales_uP | cost_of_sales |
|---|---|---|---|---|---|---|---|
| 3 | '2022-01-23' | ABC | out | 7 | 40.00 | 30.1428 | 211.00 |
| 4 | '2022-01-23' | ABC | out | 1 | 42.00 | 32.50 | 32.50 |
| 6 | '2022-01-03' | XYZ | out | 3 | 25.00 | 20.50 | 61.50 |
I have no idea how to achieve this. Any help and guidance is appreciated. Result table doesn't have to be exactly like that but the main idea is there.
Thanks!
Solution 1:[1]
The following method might not be the fastest.
But it gets the job done.
First the incoming are unfolded into a temp table.
Then, by looping over the outgoing, each incoming unit is assigned to an outgoing on a First-In-First-Out basis.
The final query then uses the results of the temp table to calculate the total & average of the incoming.
IF OBJECT_ID('tempdb..#tmpStock') IS NOT NULL DROP TABLE #tmpStock; CREATE TABLE #tmpStock ( id int identity primary key, code varchar(30), date_in date, rowN int, unitPrice decimal(10,2), rowN_out int );
-- -- Using a recursive CTE to unfold the incoming for the temp table -- with RCTE as ( select stockCode, date_, rowN, amount, unitPrice , 1 as lvl from stock_transactions where sign_ = 'in' union all select stockCode, date_, rowN, amount, unitPrice , lvl + 1 from RCTE where lvl < amount ) insert into #tmpStock (code, date_in, rowN, unitPrice) select stockCode, date_, rowN, unitPrice from RCTE order by stockCode, date_, rowN option (maxrecursion 0);
DECLARE @IdOut INT = 1; DECLARE @RowsOut INT = 0; DECLARE @code VARCHAR(30); DECLARE @amount SMALLINT; DECLARE @date DATE; DECLARE @rowN INT; DECLARE @StockOut TABLE ( id int identity primary key, code varchar(30), date_out date, rowN int, amount smallint ); insert into @StockOut (code, date_out, rowN, amount) select stockCode, date_, rowN, amount from stock_transactions where sign_ = 'out' order by stockCode, date_, rowN; SELECT @RowsOut = COUNT(*) FROM @StockOut; WHILE @IdOut <= @RowsOut BEGIN SELECT @code = code , @amount = amount , @date = date_out , @rowN = rowN FROM @StockOut WHERE id = @IdOut; ;WITH cte_in as ( select * , rn = row_number() over (order by date_in, rowN) from #tmpStock where code = @code and date_in <= @date and rowN_out is null ) UPDATE cte_in SET rowN_out = @rowN WHERE rn <= @amount; SET @IdOut = @IdOut + 1; END;
select * from #tmpStockid | code | date_in | rowN | unitPrice | rowN_out -: | :--- | :--------- | ---: | --------: | -------: 1 | ABC | 2022-01-20 | 1 | 29.20 | 3 2 | ABC | 2022-01-20 | 1 | 29.20 | 3 3 | ABC | 2022-01-20 | 1 | 29.20 | 3 4 | ABC | 2022-01-20 | 1 | 29.20 | 3 5 | ABC | 2022-01-20 | 1 | 29.20 | 3 6 | ABC | 2022-01-22 | 2 | 32.50 | 3 7 | ABC | 2022-01-22 | 2 | 32.50 | 3 8 | ABC | 2022-01-22 | 2 | 32.50 | 4 9 | XYZ | 2022-01-01 | 5 | 20.50 | 6 10 | XYZ | 2022-01-01 | 5 | 20.50 | 6 11 | XYZ | 2022-01-01 | 5 | 20.50 | 6
SELECT o.* , CAST(i.AveragePriceIn AS DECIMAL(10,2)) AS cost_of_sales_uP , i.TotalPriceIn AS cost_of_sales FROM stock_transactions o LEFT JOIN ( SELECT rowN_out , AVG(unitPrice) as AveragePriceIn , SUM(unitPrice) as TotalPriceIn FROM #tmpStock GROUP BY rowN_out ) i on i.rowN_out = o.rowN WHERE o.sign_ = 'out' ORDER BY o.rowN;rowN | date_ | stockCode | sign_ | amount | unitPrice | cost_of_sales_uP | cost_of_sales ---: | :--------- | :-------- | :---- | -----: | --------: | ---------------: | ------------: 3 | 2022-01-23 | ABC | out | 7 | 40.00 | 30.14 | 211.00 4 | 2022-01-23 | ABC | out | 1 | 42.00 | 32.50 | 32.50 6 | 2022-01-03 | XYZ | out | 3 | 25.00 | 20.50 | 61.50
Demo on db<>fiddle here
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
