Calculate Profit Based on First-In, First-Out Pricing

前端 未结 4 1604
醉话见心
醉话见心 2021-02-07 18:10

Say I have purchase and sales data for some SKUs:

po_id | sku | purchase_date    | price | qty
----------------------------------------------
    1 | 123 | 2013-         


        
相关标签:
4条回答
  • 2021-02-07 18:40

    Good question. The approach that I'm taking is to calculate the total sales. Then calculate cumulative purchases, and combine them with special logic to get the right arithmetic for the combination:

    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
    

    Note: I haven't tested this query so it might have syntax errors.

    0 讨论(0)
  • 2021-02-07 18:43

    This is really horrible since it changes a MySQL variable in the queries, but it kind of works (and takes 3 statements):

    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;
    
    0 讨论(0)
  • 2021-02-07 18:48

    setting ambient

        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)
    

    a sqlserver solution should be...

        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
    

    resultset achieved

        sku margin
        123 45,75
        456 20,00
    

    same result for sku 123 example in the problem description...

    30*1 + 28*3 + 25*2 - 20.15*5 - 17.50*1 = 45.75

    0 讨论(0)
  • 2021-02-07 18:57

    This is Oracle query but should work in any SQL. It is simplified and does not include all necessary calculations. You can add them yourself. You will see slightly diff totals as 17.50*3 not 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
    

    You can also add partition by SKU if required:

    SUM(price*qty) OVER (PARTITION BY sku ORDER BY sku)
    
    0 讨论(0)
提交回复
热议问题