根据Oracle(MRP)中的父值计算子值

时间:2016-06-24 20:04:18

标签: sql oracle

我有一个名为ORDERS的表,其中包含产品和订购数量的列表:

ORDER N° | PRODUCT | QUANTITY
1        | BICYCLE | 10
2        | BICYCLE | 3
3        | CAR     | 2
...

另一个名为COMPOSITION的表以分层方式保存第一个表中产品的所有组件的列表。

PRODUCT      | COMPONENT        | FACTOR
BICYCLE      | FRAME            | 1
FRAME        | ALUMINUM BARS    | 12
BICYCLE      | DRIVING UNIT     | 1
DRIVING UNIT | SMALL GEAR       | 1
DRIVING UNIT | BIG GEAR         | 1
DRIVING UNIT | CHAIN            | 1
DRIVING UNIT | PEDALS SET       | 2
PEDALS SET   | PEDALS           | 1
PEDALS SET   | SCREWS           | 4
...

这意味着自行车由1个框架制成,而框架又由12个铝条制成。每辆自行车也由1个驱动单元组成,每个单元由1个小型和1个大齿轮,1个链条和2个踏板组成,每个踏板组由1个踏板和4个螺丝组成。

出于简化目的,提供的示例仅适用于自行车,但该表包含所有可用产品的组件,每个层次结构中的级别数因产品而异。

我有一个名为STOCK的第三个表,其中包含上表中提到的每个组件的库存。

COMPONENT     | STOCK
BICYCLE       | 2
FRAME         | 5
ALUMINUM BARS | 26
DRIVING UNIT  | 7
...
PEDALS SET    | 0
PEDALS        | 5
SCREWS        | 10

我想使用SQL查询来确定缺少每个组件的数量来完成订单。

计算示例:

Quantity ordered of bicycles 10 + 3 = 13
Missing quantity of bicycles = 13 - 2 (stock) = 11
Missing quantity of frames = 11 * 1 (1 per bicycle) - 5 (stock) = 6
...
Missing quantity of driving units = 11 * 1 (1 per bicycle) - 7 (stock) = 4
Missing quantity of pedals sets 4 * 2 (2 per driving unit) - 0 (stock) = 8
Missing quantity of pedals = 8 * 1 (1 per pedals set) - 5 (stock) = 3
Missing quantity of screws = 8 * 4 (4 per pedals set) - 10 (stock) = 22
...

所以决赛桌看起来像是:

COMPONENT    | MISSING
FRAMES       | 6
...
DRIVING UNIT | 4
PEDALS SET   | 8
PEDALS       | 3
SCREWS       | 22
...

1 个答案:

答案 0 :(得分:0)

Oracle安装程序

CREATE TABLE composition ( PRODUCT, COMPONENT, FACTOR ) AS
SELECT 'BICYCLE',       'FRAME',            1 FROM DUAL UNION ALL
SELECT 'FRAME',         'ALUMINUM BARS',    12 FROM DUAL UNION ALL
SELECT 'BICYCLE',       'DRIVING UNIT',     1 FROM DUAL UNION ALL
SELECT 'DRIVING UNIT',  'SMALL GEAR',       1 FROM DUAL UNION ALL
SELECT 'DRIVING UNIT',  'BIG GEAR',         1 FROM DUAL UNION ALL
SELECT 'DRIVING UNIT',  'CHAIN',            1 FROM DUAL UNION ALL
SELECT 'DRIVING UNIT',  'PEDALS SET',       2 FROM DUAL UNION ALL
SELECT 'PEDALS SET',    'PEDALS',           1 FROM DUAL UNION ALL
SELECT 'PEDALS SET',    'SCREWS',           4 FROM DUAL;

CREATE TABLE orders ( ORDER_NO, PRODUCT, QUANTITY ) AS
SELECT 1, 'BICYCLE', 10 FROM DUAL UNION ALL
SELECT 2, 'BICYCLE', 3 FROM DUAL UNION ALL
SELECT 3, 'CAR',     2 FROM DUAL;

CREATE TABLE stock ( COMPONENT, STOCK ) AS
SELECT 'BICYCLE',       2 FROM DUAL UNION ALL
SELECT 'FRAME',         5 FROM DUAL UNION ALL
SELECT 'ALUMINUM BARS', 26 FROM DUAL UNION ALL
SELECT 'DRIVING UNIT',  7 FROM DUAL UNION ALL
SELECT 'PEDALS SET',    0 FROM DUAL UNION ALL
SELECT 'PEDALS',        5 FROM DUAL UNION ALL
SELECT 'SCREWS',        10 FROM DUAL;

CREATE OR REPLACE PACKAGE stock_pkg IS
  TYPE composition_table IS TABLE OF COMPOSITION%ROWTYPE;

  FUNCTION path(
    root    IN COMPOSITION.PRODUCT%TYPE,
    leaf    IN COMPOSITION.COMPONENT%TYPE
  ) RETURN composition_table PIPELINED;

  FUNCTION calculateFactor(
    root    IN COMPOSITION.PRODUCT%TYPE,
    stop_at IN COMPOSITION.PRODUCT%TYPE,
    leaf    IN COMPOSITION.COMPONENT%TYPE
  ) RETURN NUMBER;
END;
/

CREATE OR REPLACE PACKAGE BODY stock_pkg
IS
  FUNCTION path(
    root    IN COMPOSITION.PRODUCT%TYPE,
    leaf    IN COMPOSITION.COMPONENT%TYPE
  ) RETURN composition_table PIPELINED
  IS
    CURSOR cur IS
      SELECT up.product,
             up.component,
             up.factor
      FROM   (
        SELECT c.*, ROWID AS rid
        FROM   composition c
        START WITH product = root
        CONNECT BY product = PRIOR component
      ) up
      INNER JOIN
      (
        SELECT ROWID AS rid
        FROM   composition
        START WITH component = leaf
        CONNECT BY PRIOR product = component
      ) down
      ON ( up.RID = down.RID );

    r_composition COMPOSITION%ROWTYPE;
  BEGIN
    OPEN cur;

    LOOP
      FETCH cur INTO r_composition;
      EXIT WHEN cur%NOTFOUND;
      PIPE ROW( r_composition );
    END LOOP;

    CLOSE cur;
  EXCEPTION
    WHEN NO_DATA_NEEDED THEN
      CLOSE cur;
      RAISE;
  END;

  FUNCTION calculateFactor(
    root    IN COMPOSITION.PRODUCT%TYPE,
    stop_at IN COMPOSITION.PRODUCT%TYPE,
    leaf    IN COMPOSITION.COMPONENT%TYPE
  ) RETURN NUMBER
  IS
    factor COMPOSITION.FACTOR%TYPE;
  BEGIN
    SELECT COALESCE( ROUND(EXP(SUM(LN(factor)))), 1 )
    INTO   factor
    FROM   TABLE( stock_pkg.path( root, leaf ) )
    START WITH component = leaf AND component != stop_at
    CONNECT BY PRIOR product = component AND component != stop_at;

    RETURN factor;
  END;
END;
/

<强>查询

WITH comp AS (
  SELECT CONNECT_BY_ROOT( product ) AS root,
         component,
         stock_pkg.calculateFactor(
           root    => CONNECT_BY_ROOT( product ),
           stop_at => CONNECT_BY_ROOT( product ),
           leaf    => component
         ) AS total_factor
  FROM   composition c
  START WITH product IN ( SELECT product FROM orders )
  CONNECT BY PRIOR component = product
UNION ALL
  SELECT DISTINCT
         product,
         product,
         1
  FROM   orders
)
,multipliers AS (
  SELECT root,
         COALESCE( p.product, c.component ) AS product,
         c.component,
         c.total_factor,
         stock_pkg.calculateFactor( c.root, p.product, c.component ) AS multiplier
  FROM   comp c,
         TABLE( stock_pkg.path( c.root, c.component ) ) (+) p
)
,aggregated_stock AS (
  SELECT root,
         component,
         SUM( factored_stock ) AS aggregated_stock
  FROM   (
    SELECT m.root,
           m.component,
           s.stock * multiplier AS factored_stock
    FROM   multipliers m
           INNER JOIN
           stock s
           ON ( m.product = s.component )
    UNION ALL
    SELECT c.root,
           c.component,
           s.stock
    FROM   comp c
           INNER JOIN
           stock s
           ON ( c.component = s.component )
    WHERE  c.root != c.component
  )
  GROUP BY root, component
)
,total_orders AS (
  SELECT product,
        SUM( quantity ) AS quantity
  FROM   orders
  GROUP BY product
)
,required_stock AS (
  SELECT c.component,
         SUM( c.total_factor * o.quantity ) AS total_quantity
  FROM   comp c
         INNER JOIN
         total_orders o
         ON ( c.root = o.product )
  GROUP BY c.component
)
SELECT r.component,
       r.total_quantity - COALESCE( a.aggregated_stock, 0 ) AS missing
FROM   required_stock r
       LEFT OUTER JOIN
       aggregated_stock a
       ON ( r.component = a.component );

<强>输出

COMPONENT        MISSING
------------- ----------
BIG GEAR               4 
CHAIN                  4 
PEDALS SET             8 
PEDALS                 3 
SMALL GEAR             4 
ALUMINUM BARS         46 
FRAME                  6 
BICYCLE               11 
SCREWS                22 
DRIVING UNIT           4 
CAR                    2