Pregunta

Tengo un problema SQL interesante. Tengo una tabla jerárquica de piezas que hacen una factura de material. similar a ésto:

ASSEMBLY
---------
parent_part_id
part_id
quantity

Tengo la jerarquía de esta estructura con una consulta como esta:

SELECT level, part_id, quantity
from assembly
start with parent_part_id = 1
connect by parent_part_id = prior part_id;

La salida puede verse así:

level  part_id  quantity
-----  -------  ---------
1      2        2
2      3        10
1      4        2
2      5        1    
3      3        5

Hasta ahora, todo bien.

La pregunta es esta: ¿cómo calculo el número total de cada parte requerida para hacer el ensamblaje de nivel superior (Parte 1)?

Agrupar este resultado establecido por parte y sumar la cantidad no es correcta, ya que la cantidad debe multiplicarse por la cantidad de la parte inmediatamente por encima de la parte actual en la jerarquía, recursivamente en el árbol.

Estoy pensando que esta es una función de retraso, pero tener problemas para visualizarla.

Editar: resultados esperados:

part_id  quantity
-------  --------
2        2
3        30
4        2
5        2

más edición: obtengo resultados interesantes con esta consulta

SELECT rownum, level lvl, part_id, quantity, unit_of_measure
                , connect_by_isleaf || sys_connect_by_path(quantity,'*') math
            from assembly
            start with parent_part_id = 1
            connect by parent_part_id = prior part_id

La columna matemática devuelve una representación de cadena del cálculo que quiero realizar :) Por ejemplo, puede decir:

1*1*2*10

O algo similar y apropiado ... quizás hacer una función para analizar esto y devolver el resultado resolverá el problema. ¿Alguien piensa que esto es indignante?

¿Fue útil?

Solución

En Oracle 11 R2 es posible con un common table expression:

Los datos de prueba:

--  drop table assembly;

create table assembly (
  part_id              number, 
  parent_part_id       number,
  quantity             number
);

insert into assembly values (2, 1,  2);
insert into assembly values (3, 2, 10);
insert into assembly values (4, 1,  2);
insert into assembly values (5, 4,  1);
insert into assembly values (3, 5,  5);

La declaración de selección:

select 
  part_id, 
  sum(quantity_used) as quantity
from (
  with assembly_hier (lvl, part_id, quantity, quantity_used) as (
    select 
      1        lvl,
      part_id,
      quantity ,
      quantity        quantity_used
    from
      assembly
    where
      parent_part_id = 1 
  union all
    select
      assembly_hier.lvl      + 1 lvl,
      assembly     .part_id,
      assembly     .quantity,
      assembly_hier.quantity_used * assembly.quantity quantity_used
    from
      assembly_hier, assembly
    where
      assembly_hier.part_id = assembly.parent_part_id
  )
  select * from assembly_hier
)
group by part_id
order by part_id;

Editar Antes de Ora11R2, podría funcionar con un model clause:

select 
  part_id,
  sum(quantity) quantity 
from (
  select
    lvl
    parent_part_id,
    part_id,
    quantity
  from (
    select 
      lvl,
      parent_part_id,
      part_id,
      quantity
    from (
      select  
        rownum r, 
        level lvl, 
        parent_part_id,
        part_id, 
        quantity
      from 
        assembly
      start with parent_part_id = 1
      connect by parent_part_id = prior part_id
    )
  )
  model
    dimension by (lvl, part_id)
    measures (quantity, parent_part_id)
    rules upsert (
       quantity[     any, any          ] order by lvl, part_id =   quantity[cv(lvl)  , cv(part_id)] * 
                                          nvl( quantity[cv(lvl)-1,    parent_part_id[cv(lvl), cv(part_id)] ], 1)
    )
)
group by part_id
order by part_id;

Editar II Otra posibilidad sería sumar los logaritmos de cantidad y luego tomar el exponente de la suma:

select 
  part_id,
  sum(quantity) quantity
from (
  select 
    part_id,
    exp(sum(quantity_ln) over (partition by new_start order by r)) quantity
  from (
    select 
      r,
      lvl,
      part_id,
      quantity_ln,
      sum(new_start) over(order by r) new_start
    from (
      select 
        rownum r, 
        level lvl, 
        part_id, 
        ln(quantity) quantity_ln,
        nvl(lag(connect_by_isleaf,1) over (order by rownum),0) new_start
      from assembly
      start with parent_part_id = 1
      connect by parent_part_id = prior part_id
    )
  )
)
group by part_id
order by part_id
;

Otros consejos

Terminé aquí: esto funciona en Oracle 10 y 11, el Connect_By_isleaf se puede usar para ajustar la lógica si desea sumar solo las hojas o todos los nodos.

select part_id,  new_rec.quantity*sum(math_calc( math,2)) m, unit_of_measure
from ( SELECT rownum, level lvl, part_id, quantity, unit_of_measure
            , connect_by_isleaf || sys_connect_by_path(quantity,'*') math
from assembly
start with parent_part_id = new_rec.part_id
connect by parent_part_id = prior part_id ) p
group by part_id, unit_of_measure 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top