Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.misc -> Re: How to code SQL for a BOM Explosion?

Re: How to code SQL for a BOM Explosion?

From: Michel Cadot <micadot_at_netcourrier.com>
Date: Mon, 23 Aug 1999 11:29:11 +0200
Message-ID: <7pr4af$d07$1@oceanite.cybercable.fr>


Glenn Heinze a écrit dans le message <7pgpmh$lgt$1_at_cougar.golden.net>...
>If I have a table of finished goods named Product like this:
>
>ProductID Name
>======== ====
>1 Product A
>2 Product B
>3 Product C
>4 Product D
>
>and a components table like this:
>
>ProductID PartID Count
>======== ===== =====
>1 2 1
>1 3 2
>3 4 3
>
>I would like to report that "Product A" is composed of 1 piece of "Product
>B" and 6 pieces of "Product D" (ie 2 pieces of "Product C" each of which is
>itself composed of 3 pieces of "Product D".
>
>There is no fixed depth to the recursion.
>
>Is there a way to write a SELECT statement for this where a can avoid
>procedures? Is this where I could use the "connect by ... prior..." clause?
>Is there a way to do it with "standard" sql? Or perhaps the two table
>structure of "Product" and "Component" is not the most elegant approach?
>
>Thanks in advance for any help
>-Glenn
>
>

It is not possible to write a simple SELECT statement because of several limitations :
- you cannot have the root, the leaf and all the path in the same result row   with a CONNECT BY ;
- you cannot use GROUP BY or join with the CONNECT BY ; - there is no PROD group function to calculate the product of values like the   SUM one calculate the sum and even less a SUM/PROD function you need in your   case (if, for instance, "Product A" is also directly made with 2 pieces of   "Product D", total of pieces of "Product D" = (2*3)+2 = 8). ...

I got a result, if you admit to create a simple package to simulate a prod group function and an intermediate table.

Here my script following by the execution report (i add an intermediate view to show you the result at this level and to simplify the following query but this is not mandatory):

/* Table of products */
drop table product;
create table product (pid number, name varchar2(20));

insert into product values (1, 'Product A');
insert into product values (2, 'Product B');
insert into product values (3, 'Product C');
insert into product values (4, 'Product D');
insert into product values (5, 'Product E');
insert into product values (6, 'Product F');
insert into product values (7, 'Product G');
insert into product values (8, 'Product H');
commit;
/* Table of components */
drop table component;
create table component (pid number, cid number, cnt number);
insert into component values (5, 2, 3);
insert into component values (4, 7, 5);
insert into component values (3, 8, 10);
insert into component values (3, 4, 3);
insert into component values (3, 7, 7);
insert into component values (1, 3, 2);
insert into component values (1, 2, 1);
commit;
/* Package to simulate grouping */
create or replace package pkg_group as

   /* Generate a group number */
   group_nb number;
   function grpnb (new_group integer) return number;    pragma restrict_references(grpnb,WNDS,RNDS);    /* Simulate a grouping product */
   group_prod number;
   function grpprod (value number, new_group integer) return number;    pragma restrict_references(grpprod,WNDS,RNDS); end pkg_group;
/
create or replace package body pkg_group as

   /* Generate a group number */
   function grpnb (new_group integer) return number    is
   begin

      if ( new_group <> 0 ) then
         group_nb := group_nb + 1;
      end if;
      return group_nb;

   end;
   /* Simulate a grouping product */
   function grpprod (value number, new_group integer) return number    is
   begin
      if ( new_group <> 0 ) then
         group_prod := value;
      else
         group_prod := group_prod * value;
      end if;
      return group_prod;

   end;
end pkg_group;
/
/* Create an intermediate view to simplify the */
/* text of the creation of the result table    */
create or replace view v as
select rownum nb, level lvl, pid, cid, cnt from component
connect by prior cid = pid
start with pid in

   (select pid from product group by pid)

/
/* Make the result with only the leaves        */
/* Leaves are components who have no component */
exec pkg_group.group_nb := 0;
drop table t;
create table t as
select a.pid, b.cid, c.nb,
       pkg_group.grpnb(decode(c.nb, a.nb, 1, 0)) grp,
       pkg_group.grpprod(c.cnt, decode(c.nb, a.nb, 1, 0)) cnt
from v c, v b, v a
where /* Select the composed products */

      a.lvl = 1
  and /* Search all the components of the selected product */

      ( b.nb >= a.nb
      and b.nb < (select nvl(min(d.nb),999999) from v d
                  where d.lvl = 1 and d.nb > a.nb ) )
  and /* Search the intermediate components */
      ( c.nb between a.nb and b.nb
      and ( c.nb = b.nb
          or ( c.lvl < b.lvl
             and not exists ( select 1 from v d
                              where d.nb > c.nb and d.nb < b.nb
                                and d.lvl = c.lvl ) ) )
      )
  and /* Keep only the leaves */
      not exists ( select 1 from v d
                   where d.nb > b.nb and d.lvl > b.lvl
                     and d.nb < ( select nvl(min(e.nb),999999)
                                  from v e
                                  where e.lvl <= b.lvl
                                    and e.nb > b.nb )
                 )

/
/* Display the result */
select b.name "Product", c.name "Component", sum(cnt) "Count" from product c, product b, t a
where a.nb = ( select max(nb) from t b

               where b.grp = a.grp )
  and b.pid = a.pid
  and c.pid = a.cid
group by b.name, c.name
/

And here's the report:

SQL>select * from product order by pid
  2 /

       PID NAME

---------- --------------------
         1 Product A
         2 Product B
         3 Product C
         4 Product D
         5 Product E
         6 Product F
         7 Product G
         8 Product H

8 rows selected.

SQL>/* Content of table component */
SQL>select * from component order by pid, cid   2 /

       PID CID CNT
---------- ---------- ----------

         1          2          1
         1          3          2
         3          4          3
         3          7          7
         3          8         10
         4          7          5
         5          2          3

7 rows selected.

SQL>/* Display intermediate result */
SQL>select * from v
  2 /

        NB LVL PID CID CNT ---------- ---------- ---------- ---------- ----------

         1          1          1          3          2
         2          2          3          8         10
         3          2          3          4          3
         4          3          4          7          5
         5          2          3          7          7
         6          1          1          2          1
         7          1          3          8         10
         8          1          3          4          3
         9          2          4          7          5
        10          1          3          7          7
        11          1          4          7          5
        12          1          5          2          3

12 rows selected.

SQL>/* Display the result */
SQL>break on Product
SQL>select b.name "Product", c.name "Component", sum(cnt) "Count"
  2 from product c, product b, t a
  3 where a.nb = ( select max(nb) from t b
  4                 where b.grp = a.grp )
  5 and b.pid = a.pid
  6 and c.pid = a.cid
  7 group by b.name, c.name
  8 /
Product              Component                 Count
-------------------- -------------------- ----------
Product A            Product B                     1
                     Product G                    44
                     Product H                    20
Product C            Product G                    22
                     Product H                    10
Product D            Product G                     5
Product E            Product B                     3

7 rows selected.

This is not very beautiful. A PL/SQL procedure will make it easier! The only advantage is you can see that you can related root and its leaves of a hierarchical query. Received on Mon Aug 23 1999 - 04:29:11 CDT

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US