计算 FIFO 中的价格 SQL

Calculate out price in FIFO SQL

使用 Postgres 11

使用 FIFO,我想计算从库存中取出的物品的价格,以跟踪总库存的价值。

数据集如下:

ID  | prodno | amount_purchased | amount_taken | price | created_at
uuid  13976    10                 NULL           130     <timestamp>  
uuid  13976    10                 NULL           150     <timestamp>
uuid  13976    10                 NULL           110     <timestamp>
uuid  13976    10                 NULL           100     <timestamp>
uuid  13976    NULL                 14           ??      <timestamp>

在插入带有 amount_taken 的行之前,我需要计算 14 件商品中每件商品的平均价格,在本例中为 135,71,但如何相对计算高效?

我最初的想法是将行委托给两个临时 tables,一个 amount_taken 为空,一个不为空,然后向下计算所有行,但是鉴于此 table 可能变得相当大,相当快(因为大多数时候,只会从库存中取出 1 件物品),我担心这在短期内是一个不错的解决方案,但会减慢速度,随着 table 变大。那么,更好的互联网解决方案是什么?

鉴于此设置:

CREATE TABLE test (
    id int
    , prodno int 
    , quantity numeric
    , price numeric 
    , created_at timestamp
);
INSERT INTO test VALUES
    (1, 13976, 10,    130, NOW())
    , (2, 13976, 10,  150, NOW()+'1 hours')
    , (3, 13976, 10,  110, NOW()+'2 hours')
    , (4, 13976, 10,  100, NOW()+'3 hours')
    , (5, 13976, -14, NULL, NOW()+'4 hours')
    , (6, 13976, -1, NULL, NOW()+'5 hours')
    , (7, 13976, -10, NULL, NOW()+'6 hours')
    ;

然后SQL

SELECT id, prodno, created_at, qty_sold
    -- 5
    , round((cum_sold_cost - coalesce(lag(cum_sold_cost) over w, 0))/qty_sold, 2) as fifo_price 
    , qty_bought, prev_bought, total_cost
    , prev_total_cost
    , cum_sold_cost
    , coalesce(lag(cum_sold_cost) over w, 0) as prev_cum_sold_cost
FROM (
    SELECT id, tneg.prodno, created_at, qty_sold, tpos.qty_bought, prev_bought, total_cost, prev_total_cost
        -- 4
        , round(prev_total_cost + ((tneg.cum_sold - tpos.prev_bought)/(tpos.qty_bought - tpos.prev_bought))*(total_cost-prev_total_cost), 2) as cum_sold_cost 
    FROM (
      SELECT id, prodno, created_at, -quantity as qty_sold
          , sum(-quantity) over w as cum_sold
      FROM test
      WHERE quantity < 0
      WINDOW w AS (PARTITION BY prodno ORDER BY created_at)
    -- 1
    ) tneg 
    LEFT JOIN (
      SELECT prodno
          , sum(quantity) over w as qty_bought
          , coalesce(sum(quantity) over prevw, 0) as prev_bought
          , quantity * price as cost                              
          , sum(quantity * price) over w as total_cost
          , coalesce(sum(quantity * price) over prevw, 0) as prev_total_cost
      FROM test
      WHERE quantity > 0
      WINDOW w AS (PARTITION BY prodno ORDER BY created_at)
          , prevw AS (PARTITION BY prodno ORDER BY created_at ROWS BETWEEN unbounded preceding AND 1 preceding)
    -- 2
    ) tpos 
    -- 3
    ON tneg.cum_sold BETWEEN tpos.prev_bought AND tpos.qty_bought 
        AND tneg.prodno = tpos.prodno
    ) t
WINDOW w AS (PARTITION BY prodno ORDER BY created_at)

产量

| id | prodno | created_at                 | qty_sold | fifo_price | qty_bought | prev_bought | total_cost | prev_total_cost | cum_sold_cost | prev_cum_sold_cost |
|----+--------+----------------------------+----------+------------+------------+-------------+------------+-----------------+---------------+--------------------|
|  5 |  13976 | 2019-03-07 21:07:13.267218 |       14 |     135.71 |         20 |          10 |       2800 |            1300 |       1900.00 |                  0 |
|  6 |  13976 | 2019-03-07 22:07:13.267218 |        1 |     150.00 |         20 |          10 |       2800 |            1300 |       2050.00 |            1900.00 |
|  7 |  13976 | 2019-03-07 23:07:13.267218 |       10 |     130.00 |         30 |          20 |       3900 |            2800 |       3350.00 |            2050.00 |

  1. tneg 包含有关已售数量的信息

    | id | prodno | created_at                 | qty_sold | cum_sold |
    |----+--------+----------------------------+----------+----------|
    |  5 |  13976 | 2019-03-07 21:07:13.267218 |       14 |       14 |
    |  6 |  13976 | 2019-03-07 22:07:13.267218 |        1 |       15 |
    |  7 |  13976 | 2019-03-07 23:07:13.267218 |       10 |       25 |
    
  2. tpos 包含有关购买数量的信息

    | prodno | qty_bought | prev_bought | cost | total_cost | prev_total_cost |
    |--------+------------+-------------+------+------------+-----------------|
    |  13976 |         10 |           0 | 1300 |       1300 |               0 |
    |  13976 |         20 |          10 | 1500 |       2800 |            1300 |
    |  13976 |         30 |          20 | 1100 |       3900 |            2800 |
    |  13976 |         40 |          30 | 1000 |       4900 |            3900 |
    
  3. 我们将 tneg 中的行与 tpos 中的行匹配,条件是 cum_soldqty_boughtprev_bought 之间。 cum_sold是累计卖出量,qty_bought是累计买入量,prev_boughtqty_bought的前值。

    | id | prodno | created_at                 | qty_sold | cum_sold | qty_bought | prev_bought | total_cost | prev_total_cost | cum_sold_cost |
    |----+--------+----------------------------+----------+----------+------------+-------------+------------+-----------------+---------------|
    |  5 |  13976 | 2019-03-07 21:07:13.267218 |       14 |       14 |         20 |          10 |       2800 |            1300 |       1900.00 |
    |  6 |  13976 | 2019-03-07 22:07:13.267218 |        1 |       15 |         20 |          10 |       2800 |            1300 |       2050.00 |
    |  7 |  13976 | 2019-03-07 23:07:13.267218 |       10 |       25 |         30 |          20 |       3900 |            2800 |       3350.00 |
    
  4. 分数

    ((tneg.cum_sold - tpos.prev_bought)/(tpos.qty_bought - tpos.prev_bought)) as frac
    

    测量 cum_soldqty_boughtprev_bought 之间的距离。我们用这个分数来计算 cum_sold_cost,与购买 cum_sold 项相关的累计成本。 cum_sold_cost 位于 prev_total_costtotal_cost 之间的 frac 距离。

  5. 一旦获得cum_sold_cost,您就拥有了计算边际 FIFO 单价所需的一切。 对于 tneg 的每一行,cum_sold_cost 与其先前值之间的差异是 qty_sold 的成本。 先进先出价格就是这个成本和qty_sold.

  6. 的比率