Mysql 根据先进先出定价计算利润
假设我有一些SKU的购买和销售数据:Mysql 根据先进先出定价计算利润,mysql,sql,sql-server,Mysql,Sql,Sql Server,假设我有一些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
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购买,未售出。
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 123示例的结果相同
30*1+28*3+25*2-20.15*5-17.50*1=45.75这是个好问题。我采用的方法是计算总销售额。然后计算累计购买量,并将其与特殊逻辑相结合,以获得正确的组合算法:
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
注意:我还没有测试这个查询,所以它可能有语法错误。这是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)
这真的很可怕,因为它在查询中更改了一个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;
你能澄清一下吗?一个表格是购买或批发价格,另一个是零售销售数据?那么,你将有效批发价定为17.50/3美元的5/1?然后,您想将当时的活动价格与活动期间的零售销售数据进行比较?这也有助于了解您正在使用的RDBMS产品。像这样的问题肯定会从特定于供应商的SQL功能中受益,例如CTE、分区等。@恶心,没错,这是批发购买数据和零售销售数据。我们在SQL Server和MySQL中都有这些数据,但是如果可能的话,一个通用的SQL解决方案将是最好的。这个解决方案将非常类似于先进先出的FIFO证券定价问题。你找过这样的东西吗?@不,有什么你能指给我的吗?他们想要按先进先出的顺序出售的存货的保证金,他们想要知道剩余的存货。他们不想在利润结果中考虑剩余的存货,他们想知道他们剩下的存货以及他们为这些特定的单位支付了多少。@Love2Learn-和?为什么我要打破我的头来计算所有这些。。。?我展示了做任何计算的简单方法。让用户做数学。顺序是什么?这篇文章没有提到。我看到的只是我遵循的一个公式。戈登很棒,我真的这么认为,但你能读懂吗?为什么过于复杂?首先,mysql没有完整的外部连接。联合是解决这个问题的一种方法。@ebyrob。我明白了。完全的外部连接甚至不需要,所以我将其切换为简单的内部连接。它就在那里,只是因为我担心丢失SKU。我不得不修改案例陈述以获得我想要的,但这似乎奏效了。注:费用总额可以简化为:SUMprice*GREATESTLEASTqty,totalqty-cumeqty-qty,0无需案例说明。
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;