Рассчитать цену в FIFO SQL - PullRequest
       23

Рассчитать цену в FIFO SQL

0 голосов
/ 07 марта 2019

Использование 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, но как рассчитать это относительно эффективно?

Моя первоначальная идея состояла в том, чтобы делегировать строки в две временные таблицы, одну, где amount_taken равен нулю, и другую, где она не равна нулю, а затем вычислить все строки вниз, но, учитывая, что эта таблица может стать довольно большой, довольно быстрой (так как большую часть времени из инвентаря будет взят только 1 предмет), я боюсь, что это было бы неплохим решением в краткосрочной перспективе, но замедлилось бы, так как таблица становится больше. Итак, что является лучшим решением интернет?

1 Ответ

3 голосов
/ 08 марта 2019

С учетом этой настройки:

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_sold находится между qty_bought и prev_bought. cum_sold - совокупная сумма продаж, qty_bought - совокупная сумма покупки, prev_bought - предыдущее значение qty_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_sold лежит между qty_bought и prev_bought. Мы используем эту дробь для вычисления cum_sold_cost, совокупная стоимость, связанная с покупкой cum_sold предметов. cum_sold_cost лежит frac расстояние между prev_total_cost и total_cost.

  5. Как только вы получите cum_sold_cost, у вас есть все, что вам нужно для расчета предельных цен за единицу FIFO. Для каждой строки tneg разница между cum_sold_cost и ее предыдущим значением равна стоимости qty_sold. Цена FIFO - это просто соотношение этой стоимости и qty_sold.

...