Home > Enterprise >  Oracle sql using previous rows data in calculations
Oracle sql using previous rows data in calculations

Time:12-17

I have a table T1 with 06 columns and want to get new two columns using a select query. Here's T1 with two extra columns (STOCK, WAUC) that i want to get :

CREATE TABLE T1 (MOUVEMENT NUMBER(2), OPERATION VARCHAR2(5), ITEM VARCHAR2(5), INPUT_QTY NUMBER(6, 2), OUTPUT_QTY NUMBER(6, 2), INPUT_PRICE NUMBER(6, 2), STOCK NUMBER(6, 2), WAUC NUMBER(6, 2));
INSERT ALL
INTO T1 VALUES(1, 'I', 'A', 1500,  0,      5,      1500,       5)
INTO T1 VALUES(2, 'I', 'A', 700,   0,      6,      2200,       5.31)
INTO T1 VALUES(3, 'O', 'A', 0,     800,    0,      1400,       5.31)
INTO T1 VALUES(4, 'I', 'A', 1000,  0,      5,      2400,       5.18)
INTO T1 VALUES(5, 'O', 'A', 0,     500,    0,      1900,       5.18)
INTO T1 VALUES(6, 'I', 'A', 1000,  0,      7,      2900,       5.8 )
INTO T1 VALUES(7, 'I', 'A', 2000,  0,      7,      4900,       6.28)
INTO T1 VALUES(8, 'I', 'A', 5000,  0,      7,      5400,       6.34)
INTO T1 VALUES(9, 'O', 'A', 0,     1000,   0,      4400,       6.34)
INTO T1 VALUES(10, 'I','A', 1000,  0,      5,      5400,       6.09)
SELECT 1 FROM DUAL;

WAUC is like weighted average unit cost to valorise our stock.

  • In case first record : STOCK = INPUT and WAUC = INPUT_PRICE;
  • In case new INPUT operation : new WAUC should be : (last generated WAUC * last generated stock) (current INPUT * current INPUT_PRICE)) / current generated STOCK.

Ex for 2nd row : WAUC = ((5 * 1500) (700 * 6)) / 2200 = 5.31

  • In case new OUTPUT operation : WAUC should be last generated WAUC.

Ex for 3rd row : WAUC = last generated WAUC (5.31) of the same ITEM A.

  • Means, WAUC should be changed every new INPUT operation.
  • In my opinion, STOCK and WAUC should be generated on the fly, not as records, besause otherwise, only one accidently wrong INPUT_PRICE, will cause wrong next WAUC(s) -> wrong next calculation(s) -> (wrong work).

how can I achieve this? Thanks in advance.

CodePudding user response:

Your logic is textbook example of need for model clause and can be rewritten to that clause almost as you verbosely specified (note the model clause is a beast, to learn more about it see here or here or here):

with t1 (mouvement, operation, item, input_qty, output_qty, input_price, stock_expected, wauc_expected) as (
 select 1, 'I', 'A', 1500,  0,      5,      1500,       5    from dual union all
 select 2, 'I', 'A', 700,   0,      6,      2200,       5.31 from dual union all
 select 3, 'O', 'A', 0,     800,    0,      1400,       5.31 from dual union all
 select 4, 'I', 'A', 1000,  0,      5,      2400,       5.18 from dual union all
 select 5, 'O', 'A', 0,     500,    0,      1900,       5.18 from dual union all
 select 6, 'I', 'A', 1000,  0,      7,      2900,       5.8  from dual union all
 select 7, 'I', 'A', 2000,  0,      7,      4900,       6.28 from dual union all
 select 8, 'I', 'A', 500,  0,      7,      5400,       6.34 from dual union all
 select 9, 'O', 'A', 0,     1000,   0,      4400,       6.34 from dual union all
 select 10, 'I','A', 1000,  0,      5,      5400,       6.09 from dual
)
select * from (
  select t1.*, 0 as stock_actual, 0 as wauc_actual from t1
)
model
  dimension by (row_number() over (order by mouvement) as rn)
  measures (mouvement, operation, item, input_qty, output_qty, input_price, stock_expected, wauc_expected, stock_actual, wauc_actual)
  rules (
    stock_actual[any] = coalesce(stock_actual[cv(rn) - 1], 0)   case operation[cv(rn)]
      when 'I' then input_qty[cv(rn)]
      when 'O' then -output_qty[cv(rn)]
    end,
    wauc_actual[any] = case
      when cv(rn) = 1
        then input_price[cv(rn)]
      when operation[cv(rn)] = 'I'
        then trunc((wauc_actual[cv(rn) - 1] * stock_actual[cv(rn) - 1]   input_qty[cv(rn)] * input_price[cv(rn)]) / stock_actual[cv(rn)], 2)
      when operation[cv(rn)] = 'O'
        then wauc_actual[cv(rn) - 1]
    end
  )
order by mouvement

(I changed typo in operation=5000->500 for mouvement=8 and added truncation to 2 digits - both I guessed from your expected results.)

Db fiddle here.

Note that simple analytic functions are not sufficient for computation of wauc because they have access only to previous values of column of input dataset, not the values of column being computed by the function itself. For stock it would be possible using running totals of sum(input_qty) over (order by mouvement) - sum(output_qty) over (order by mouvement) but for wauc there is hardly any explicit formula.

  • Related