Рассчитать прибыль, основанную на ценах First-In, First-Out
Скажем, у меня есть данные о покупке и продаже для некоторых SKU:
po_id | sku | purchase_date | price | qty
----------------------------------------------
1 | 123 | 2013-01-01 12:25 | 20.15 | 5
2 | 123 | 2013-05-01 15:45 | 17.50 | 3
3 | 123 | 2013-05-02 12:00 | 15.00 | 1
4 | 456 | 2013-06-10 16:00 | 60.00 | 7
sale_id | sku | sale_date | price | qty
------------------------------------------------
1 | 123 | 2013-01-15 11:00 | 30.00 | 1
2 | 123 | 2013-01-20 14:00 | 28.00 | 3
3 | 123 | 2013-05-10 15:00 | 25.00 | 2
4 | 456 | 2013-06-11 12:00 | 80.00 | 1
Как я могу найти маржу продаж через SQL, если они продаются в том порядке, в котором они были приобретены? Например, край для sku 123 равен
30*1 + 28*3 + 25*2 - 20.15*5 - 17.50*1
с 2 купленными в 17.50 и 1, купленные в 15.00, остались непроданными.
Ответы
Ответ 1
Хороший вопрос. Подход, который я принимаю, - это рассчитать общий объем продаж. Затем вычислите кумулятивные покупки и объедините их со специальной логикой, чтобы получить правильную арифметику для комбинации:
select s.sku,
(MarginPos - SUM(case when s.totalqty < p.cumeqty - p.qty then p.price * p.qty
when s.totalqty between p.cumeqty - p.qty and p.qty
then s.price * (s.totalqty - (p.cumeqty - p.qty))
else 0
end)
) as Margin
from (select s.sku, SUM(price*qty) as MarginPos, SUM(qty) as totalqty
from sales s
) s left outer join
(select p.*,
(select SUM(p.qty) from purchase p2 where p2.sku = p.sku and p2.sale_id <= p.sale_id
) as cumeqty
from purchase s
)
on s.sku = p.sku
group by s.sku, MarginPos
Примечание. Я не тестировал этот запрос, чтобы он мог иметь синтаксические ошибки.
Ответ 2
настройка окружающей среды
declare @purchased table (id int,sku int,dt date,price money,qty int)
declare @sold table (id int,sku int,dt date,price money,qty int)
insert into @purchased
values( 1 , 123 , '2013-01-01 12:25' , 20.15 , 5)
,(2 , 123 , '2013-05-01 15:45' , 17.50 , 3)
,(3 , 123 , '2013-05-02 12:00' , 15.00 , 1)
,(4 , 456 , '2013-06-10 16:00' , 60.00 , 7)
insert into @sold
values(1 , 123 , '2013-01-15 11:00' , 30.00 , 1)
,(2 , 123 , '2013-01-20 14:00' , 28.00 , 3)
,(3 , 123 , '2013-05-10 15:00' , 25.00 , 2)
,(4 , 456 , '2013-06-11 12:00' , 80.00 , 1)
Решение sqlserver должно быть...
with cte_sold as (select sku,sum(qty) as qty, SUM(qty*price) as total_value
from @sold
group by sku
)
,cte_purchased as (select id,sku,price,qty
from @purchased
union all select id,sku,price,qty-1 as qty
from cte_purchased
where qty>1
)
,cte_purchased_ordened as(select ROW_NUMBER() over (partition by sku order by id,qty) as buy_order
,sku
,price
,1 as qty
from cte_purchased
)
select P.sku
,S.total_value - SUM(case when P.buy_order <= S.qty then P.price else 0 end) as margin
from cte_purchased_ordened P
left outer join cte_sold S
on S.sku = P.sku
group by P.sku,S.total_value,S.qty
достигнуты результаты
sku margin
123 45,75
456 20,00
тот же результат для примера sku 123 в описании проблемы...
30 * 1 + 28 * 3 + 25 * 2 - 20,15 * 5 - 17,50 * 1 = 45,75
Ответ 3
Это действительно ужасно, так как он изменяет переменную MySQL в запросах, но она вроде работает (и принимает 3 оператора):
select
@income := sum(price*qty) as income,
@num_bought := cast(sum(qty) as unsigned) as units
from sale
where sku = 123
;
select
@expense := sum(expense) as expense,
sum(units) as units
from (select
price * least(@num_bought, qty) as expense,
least(@num_bought, qty) as units,
@num_bought := @num_bought - least(@num_bought, qty)
from purchase
where sku = 123 and @num_bought > 0
order by po_id
) as a
;
select round(@income - @expense, 2) as profit_margin;
Ответ 4
Это запрос Oracle, но он должен работать в любом SQL. Он упрощен и не включает в себя все необходимые вычисления. Вы можете добавить их сами. Вы увидите незначительные значения разницы как 17.50 * 3, а не 17.50 * 1:
SELECT po_sku AS sku, po_total, sale_total, (po_total-sale_total) Margin
FROM
(
SELECT SUM(price*qty) po_total, sku po_sku
FROM stack_test
GROUP BY sku
) a,
(
SELECT SUM(price*qty) sale_total, sku sale_sku
FROM stack_test_sale
GROUP BY sku
) b
WHERE po_sku = sale_sku
/
SKU PO_TOTAL SALE_TOTAL MARGIN
---------------------------------------------------
123 168.25 164 4.25
456 420 80 340
Вы также можете добавить раздел с помощью SKU:
SUM(price*qty) OVER (PARTITION BY sku ORDER BY sku)