Home > Enterprise >  Use oracle LAG function data in calculations
Use oracle LAG function data in calculations

Time:12-15

I want get STOCK and weighted average unit cost WAUC.

TABLE T1:

ROW ITEM IN OUT PRICE STOCK WAUC
1 A 1000 - 20 1000 20
2 A 2000 - 25 - -
3 A 1500 - 15 - -
4 A 500 - 20 - -

I have the first 5 columns and the first two records of last two columns, and want to get the rest of last two columns STOCK and WAUC.

WAUC = ((PREVIOUS_PRICE * PREVIOUS_STOCK)   (CURRENT_IN * CURRENT_PRICE)) / CURRENT_STOCK

So, I write the query:

SELECT ROW,
       SUM(IN - OUT) OVER(ORDER BY ROW) STOCK,
       ((LAG(STOCK * WAUC) OVER (ORDER BY ROW))   (IN * PRICE)) / STOCK AS WAUC
FROM T1

What I want is :

ROW ITEM IN OUT PRICE STOCK WAUC
1 A 1000 - 20 1000 20
2 A 2000 - 25 3000 23.33
3 A 1500 - 15 4500 20.55
4 A 500 - 20 5000 20.49

In other words, I want to use LAG results in calculation data.

CodePudding user response:

Your problem has nothing to do with "lag".

Using MT0's sample data:

select "ROW", item, "IN", "OUT", price,
       sum(nvl("IN", 0) - nvl("OUT", 0)) 
           over (partition by item order by "ROW") as stock,
       round(sum((nvl("IN", 0) - nvl("OUT", 0)) * price)
           over (partition by item order by "ROW") 
       / sum(nvl("IN", 0) - nvl("OUT", 0))
           over (partition by item order by "ROW"), 2 ) as wauc
from   t1
;

   ROW ITEM     IN    OUT  PRICE  STOCK   WAUC
------ ---- ------ ------ ------ ------ ------
     1 A      1000            20   1000     20
     2 A      2000            25   3000  23.33
     3 A      1500            15   4500  20.56
     4 A       500            20   5000   20.5

CodePudding user response:

Your formula should be:

WAUC = (
         PREVIOUS_WAUC * PREVIOUS_STOCK
         (CURRENT_IN - CURRENT_OUT) * CURRENT_PRICE
       )
       / CURRENT_STOCK

You can use a MODEL clause (with some extra measurements to make the calculation simpler):

SELECT "ROW", item, "IN", "OUT", price, stock, wauc
FROM   t1
MODEL
  DIMENSION BY ("ROW")
  MEASURES (item, "IN", "OUT", price, 0 AS change, 0 AS stock, 0 AS total, 0 AS wauc)
  RULES (
    change["ROW"] = COALESCE("IN"[cv()], 0) - COALESCE("OUT"[cv()], 0),
    stock["ROW"]  = change[cv()]   COALESCE(stock[cv()-1], 0),
    total["ROW"]  = change[cv()] * price[cv()]   COALESCE(total[cv()-1], 0),
    wauc["ROW"]   = total[cv()] / stock[cv()]
);

Or, from Oracle 12, using MATCH_RECOGNIZE:

SELECT "ROW",
       item,
       "IN",
       "OUT",
       price,
       total_stock AS stock,
       total_cost / total_stock AS wauc
FROM   t1
MATCH_RECOGNIZE(
  ORDER BY "ROW"
  MEASURES
    SUM(COALESCE("IN", 0) - COALESCE("OUT", 0)) AS total_stock,
    SUM((COALESCE("IN", 0) - COALESCE("OUT", 0))*price) AS total_cost
  ALL ROWS PER MATCH
  PATTERN (all_rows )
  DEFINE
    all_rows AS 1 = 1
)

Or analytic functions:

SELECT "ROW",
       item,
       "IN",
       "OUT",
       price,
       SUM(COALESCE("IN",0) - COALESCE("OUT", 0)) OVER (ORDER BY "ROW")
         AS stock,
       SUM((COALESCE("IN",0) - COALESCE("OUT", 0))*price) OVER (ORDER BY "ROW")
        / SUM(COALESCE("IN",0) - COALESCE("OUT", 0)) OVER (ORDER BY "ROW")
        AS wauc
FROM   t1

Which, for the sample data:

CREATE TABLE t1 ("ROW", ITEM, "IN", "OUT", PRICE, STOCK, WAUC) AS
SELECT 1, 'A', 1000, CAST(NULL AS NUMBER), 20, CAST(NULL AS NUMBER), CAST(NULL AS NUMBER) FROM DUAL UNION ALL
SELECT 2, 'A', 2000, NULL, 25, NULL, NULL FROM DUAL UNION ALL
SELECT 3, 'A', 1500, NULL, 15, NULL, NULL FROM DUAL UNION ALL
SELECT 4, 'A',  500, NULL, 20, NULL, NULL FROM DUAL;

All output:

ROW ITEM IN OUT PRICE STOCK WAUC
1 A 1000 20 1000 20
2 A 2000 25 3000 23.33333333333333333333333333333333333333
3 A 1500 15 4500 20.55555555555555555555555555555555555556
4 A 500 20 5000 20.5

Note: ROW, IN and OUT are keywords and you should not use them as identifiers as you would have to use quoted identifiers everywhere they occur.

db<>fiddle here

  • Related