SQL中的库存平均成本计算

时间:2011-03-22 19:39:37

标签: sql oracle oracle10g

我想用平均值计算库存成本,我有点卡在这里......

考虑一个简单的交易表tr :( ids是自动增量,负数量表示卖出交易)

order_id | volume | price | type
       1 |   1000 |   100 | B
       2 |   -500 |   110 | S
       3 |   1500 |    80 | B
       4 |   -100 |   150 | S
       5 |   -600 |   110 | S
       6 |    700 |   105 | B

现在我想知道每笔交易后的总交易量和总成本。困难在于销售正确。卖出总是以此时的平均成本计算(即卖出价格在这里实际上并不相关),因此交易订单在这里很重要。

最理想的结果是:

order_id | volume | price | total_vol | total_costs | unit_costs
       1 |   1000 |   100 |      1000 |      100000 |        100
       2 |   -500 |   110 |       500 |       50000 |        100
       3 |   1500 |    80 |      2000 |      170000 |         85
       4 |   -100 |   150 |      1900 |      161500 |         85
       5 |   -600 |   110 |      1300 |      110500 |         85
       6 |    700 |   105 |      2000 |      184000 |         92

现在,另一方面,total_vol很容易sum(volume) over (...),总费用。我玩过窗口函数,但除非我遗漏了一些非常明显(或非常聪明)的东西,否则我认为不能单独使用窗口函数...

任何帮助将不胜感激。 :)

更新

这是我最终使用的代码,两个答案的组合(数据模型比上面的简化示例复杂一点,但你明白了):

select ser_num
  , tr_id
  , tr_date
  , action_typ
  , volume
  , price
  , total_vol
  , trunc(total_costs,0) total_costs
  , trunc(unit_costs,4) unit_costs
from itt
  model
    partition by (ser_num)
    dimension by (row_number() over (partition by ser_num order by tr_date, tr_id) rn)
    measures (tr_id, tr_date, volume, price, action_typ, 0 total_vol, 0 total_costs, 0 unit_costs)
    rules automatic order 
    ( total_vol[ANY] order by rn
      = nvl(total_vol[cv()-1],0) + 
        decode(action_typ[cv()], 'Buy', 1,  'Sell', -1) * volume[cv()]
    , total_costs[ANY] order by rn
      = case action_typ[cv()]
          when 'Buy' then volume[cv()] * price[cv()] + nvl(total_costs[cv()-1],0)
          when 'Sell' then total_vol[cv()] * nvl(unit_costs[cv()-1],price[cv()])
        end
    , unit_costs[ANY] order by rn
      = decode(total_vol[cv()], 0, unit_costs[cv()-1], 
        total_costs[cv()] / total_vol[cv()])
    )
order by ser_num, tr_date, tr_id 

一些观察结果:

  • 当使用分区和对前一个单元格(cv()-1)的引用时,维度必须以与整个模型子句相同的方式进行分区(这也是为什么使用iteration_number可能会很棘手)
  • 只要您在规则上指定正确的执行顺序( order by rn edit: Automatic order自动执行此操作,此处不需要迭代)
  • 这里可能没有必要自动下单,但它不能受伤。

2 个答案:

答案 0 :(得分:15)

您可以使用MODEL子句执行此递归计算

创建样本表并插入数据

create table costs (order_id int, volume int, price numeric(16,4), type char(1));

insert into costs (order_id, volume, price) values (1,1000,100);
insert into costs (order_id, volume, price) values (2,-500,110);
insert into costs (order_id, volume, price) values (3,1500,80);
insert into costs (order_id, volume, price) values (4,-100,150);
insert into costs (order_id, volume, price) values (5,-600,110);
insert into costs (order_id, volume, price) values (6,700,105);

查询已编辑更改rules iterate(1000)rules automatic order实现了MODEL子句,因为它打算正常运行,即从上到下顺序执行。查询从0.44s到0.01s!)

select order_id, volume, price, total_vol, total_costs, unit_costs
    from (select order_id, volume, price,
                 volume total_vol,
                 0.0 total_costs,
                 0.0 unit_costs,
                 row_number() over (order by order_id) rn
          from costs order by order_id)
   model
         dimension by (order_id)
         measures (volume, price, total_vol, total_costs, unit_costs)
         rules automatic order -- iterate(1000)
         ( total_vol[any] = volume[cv()] + nvl(total_vol[cv()-1],0.0),
           total_costs[any] =
                    case SIGN(volume[cv()])
                    when -1 then total_vol[cv()] * nvl(unit_costs[cv()-1],0.0)
                    else volume[cv()] * price[cv()] + nvl(total_costs[cv()-1],0.0)
                    end,
           unit_costs[any] = total_costs[cv()] / total_vol[cv()]
         )
   order by order_id

输出

ORDER_ID VOLUME     PRICE      TOTAL_VOL   TOTAL_COSTS   UNIT_COSTS
1        1000       100        1000        100000        100
2        -500       110        500          50000        100
3        1500        80        2000        170000        85
4        -100       150        1900        161500        85
5        -600       110        1300        110500        85
6        700        105        2000        184000        92

本网站有一个关于MODEL子句的好教程

<小时/> 上面数据的EXCEL表格如下所示,公式向下延伸

    A         B       C      D          E                         F
 ---------------------------------------------------------------------------
1|  order_id  volume  price  total_vol  total_costs               unit_costs
2|                                   0                         0           0
3|  1           1000    100  =C4+E3     =IF(C4<0,G3*E4,F3+C4*D4)  =F4/E4
4|  2           -500    110  =C5+E4     =IF(C5<0,G4*E5,F4+C5*D5)  =F5/E5
5|  3           1500     80  =C6+E5     =IF(C6<0,G5*E6,F5+C6*D6)  =F6/E6
6|  4           -100    150  =C7+E6     =IF(C7<0,G6*E7,F6+C7*D7)  =F7/E7
7|  5           -600    110  =C8+E7     =IF(C8<0,G7*E8,F7+C8*D8)  =F8/E8
8|  6           700     105  =C9+E8     =IF(C9<0,G8*E9,F8+C9*D9)  =F9/E9

答案 1 :(得分:6)

Richard的模型子句查询存在问题。它在没有UNTIL子句的情况下进行了1000次迭代。经过四次迭代后,最终结果已经实现。接下来的996次迭代消耗CPU功率,但什么都不做。

在这里,您可以看到查询在使用当前数据集进行4次迭代后完成处理:

SQL> select order_id
  2       , volume
  3       , price
  4       , total_vol
  5       , total_costs
  6       , unit_costs
  7    from ( select order_id
  8                , volume
  9                , price
 10                , volume total_vol
 11                , 0.0 total_costs
 12                , 0.0 unit_costs
 13                , row_number() over (order by order_id) rn
 14             from costs
 15            order by order_id
 16         )
 17   model
 18         dimension by (order_id)
 19         measures (volume, price, total_vol, total_costs, unit_costs)
 20         rules iterate (4)
 21         ( total_vol[any] = volume[cv()] + nvl(total_vol[cv()-1],0.0)
 22         , total_costs[any]
 23           = case SIGN(volume[cv()])
 24             when -1 then total_vol[cv()] * nvl(unit_costs[cv()-1],0.0)
 25             else volume[cv()] * price[cv()] + nvl(total_costs[cv()-1],0.0)
 26             end
 27         , unit_costs[any] = total_costs[cv()] / total_vol[cv()]
 28         )
 29   order by order_id
 30  /

  ORDER_ID     VOLUME      PRICE  TOTAL_VOL TOTAL_COSTS UNIT_COSTS
---------- ---------- ---------- ---------- ----------- ----------
         1       1000        100       1000      100000        100
         2       -500        110        500       50000        100
         3       1500         80       2000      170000         85
         4       -100        150       1900      161500         85
         5       -600        110       1300      110500         85
         6        700        105       2000      184000         92

6 rows selected.

它需要4次迭代而不是6次,因为使用了自动顺序,每次迭代都会尝试调整所有6行。

如果您使用与行数一样多的迭代并且每次迭代只调整一行,那么您的性能会更高。您也可以跳过子查询,然后最终查询变为:

SQL> select order_id
  2       , volume
  3       , price
  4       , total_vol
  5       , total_costs
  6       , unit_costs
  7    from costs
  8   model
  9         dimension by (row_number() over (order by order_id) rn)
 10         measures (order_id, volume, price, type, 0 total_vol, 0 total_costs, 0 unit_costs)
 11         rules iterate (1000) until (order_id[iteration_number+2] is null)
 12         ( total_vol[iteration_number+1]
 13           = nvl(total_vol[iteration_number],0) + volume[iteration_number+1]
 14         , total_costs[iteration_number+1]
 15           = case type[iteration_number+1]
 16             when 'B' then volume[iteration_number+1] * price[iteration_number+1] + nvl(total_costs[iteration_number],0)
 17             when 'S' then total_vol[iteration_number+1] * nvl(unit_costs[iteration_number],0)
 18             end
 19         , unit_costs[iteration_number+1]
 20           = total_costs[iteration_number+1] / total_vol[iteration_number+1]
 21         )
 22   order by order_id
 23  /

  ORDER_ID     VOLUME      PRICE  TOTAL_VOL TOTAL_COSTS UNIT_COSTS
---------- ---------- ---------- ---------- ----------- ----------
         1       1000        100       1000      100000        100
         2       -500        110        500       50000        100
         3       1500         80       2000      170000         85
         4       -100        150       1900      161500         85
         5       -600        110       1300      110500         85
         6        700        105       2000      184000         92

6 rows selected.

希望这有帮助。

的问候,
罗布。

EDIT 一些证据可以支持我的声明:

SQL> create procedure p1 (p_number_of_iterations in number)
  2  is
  3  begin
  4    for x in 1 .. p_number_of_iterations
  5    loop
  6      for r in
  7      ( select order_id
  8             , volume
  9             , price
 10             , total_vol
 11             , total_costs
 12             , unit_costs
 13          from ( select order_id
 14                      , volume
 15                      , price
 16                      , volume total_vol
 17                      , 0.0 total_costs
 18                      , 0.0 unit_costs
 19                      , row_number() over (order by order_id) rn
 20                   from costs
 21                  order by order_id
 22               )
 23         model
 24               dimension by (order_id)
 25               measures (volume, price, total_vol, total_costs, unit_costs)
 26               rules iterate (4)
 27               ( total_vol[any] = volume[cv()] + nvl(total_vol[cv()-1],0.0)
 28               , total_costs[any]
 29                 = case SIGN(volume[cv()])
 30                   when -1 then total_vol[cv()] * nvl(unit_costs[cv()-1],0.0)
 31                   else volume[cv()] * price[cv()] + nvl(total_costs[cv()-1],0.0)
 32                   end
 33               , unit_costs[any] = total_costs[cv()] / total_vol[cv()]
 34               )
 35         order by order_id
 36      )
 37      loop
 38        null;
 39      end loop;
 40    end loop;
 41  end p1;
 42  /

Procedure created.

SQL> create procedure p2 (p_number_of_iterations in number)
  2  is
  3  begin
  4    for x in 1 .. p_number_of_iterations
  5    loop
  6      for r in
  7      ( select order_id
  8             , volume
  9             , price
 10             , total_vol
 11             , total_costs
 12             , unit_costs
 13          from costs
 14         model
 15               dimension by (row_number() over (order by order_id) rn)
 16               measures (order_id, volume, price, type, 0 total_vol, 0 total_costs, 0 unit_costs)
 17               rules iterate (1000) until (order_id[iteration_number+2] is null)
 18               ( total_vol[iteration_number+1]
 19                 = nvl(total_vol[iteration_number],0) + volume[iteration_number+1]
 20               , total_costs[iteration_number+1]
 21                 = case type[iteration_number+1]
 22                   when 'B' then volume[iteration_number+1] * price[iteration_number+1] + nvl(total_costs[iteration_number],0)
 23                   when 'S' then total_vol[iteration_number+1] * nvl(unit_costs[iteration_number],0)
 24                   end
 25               , unit_costs[iteration_number+1]
 26                 = total_costs[iteration_number+1] / total_vol[iteration_number+1]
 27               )
 28         order by order_id
 29      )
 30      loop
 31        null;
 32      end loop;
 33    end loop;
 34  end p2;
 35  /

Procedure created.

SQL> set timing on
SQL> exec p1(1000)

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.32
SQL> exec p2(1000)

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.45
SQL> exec p1(1000)

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.28
SQL> exec p2(1000)

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.43