Aiuto calcolo somma complessa nel set di dati gerarchica
-
24-10-2019 - |
Domanda
Ho un problema di SQL interessante. Ho una tabella gerarchica delle parti che compongono una distinta base. simile a questo:
ASSEMBLY
---------
parent_part_id
part_id
quantity
ho la gerarchia di questa struttura con una query come questa:
SELECT level, part_id, quantity
from assembly
start with parent_part_id = 1
connect by parent_part_id = prior part_id;
l'output potrebbe essere simile a questo:
level part_id quantity
----- ------- ---------
1 2 2
2 3 10
1 4 2
2 5 1
3 3 5
finora tutto bene.
la domanda è questa: come faccio a calcolare il numero totale di ogni particolare richiesto in modo da rendere il livello superiore di montaggio (parte 1)
Raggruppare questo gruppo di risultati da parte e sommando la quantità non è corretto, in quanto la quantità dovrebbe essere moltiplicato per il quantitativo della parte immediatamente sopra la parte corrente nella gerarchia, ricorsivamente sull'albero.
Sto pensando che questo è una funzione GAL, ma avendo difficoltà visualizzarlo.
Modifica: risultati attesi:
part_id quantity
------- --------
2 2
3 30
4 2
5 2
di più Modifica: ottengo risultati interessanti con questa query
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 colonna matematica restituisce una rappresentazione di stringa del calcolo voglio eseguire :) per esempio, può dire:
1*1*2*10
o qualcosa di simile e appropriato ... magari facendo una funzione per analizzare questo e restituire il risultato risolverà il problema .. qualcuno pensa che questo sia scandaloso?
Soluzione
In Oracle 11 R2 sua possibile con un common table expression
:
I dati di test:
-- 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 dichiarazione prescelta:
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;
Modifica Prima di Ora11R2, potrebbe funzionare 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;
Modifica II Un'altra possibilità sarebbe quella di sommare i logaritmi di quantità e poi prendere l'esponente della somma:
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
;
Altri suggerimenti
Ho finito qui: questo funziona su Oracle 10 e 11, il connect_by_isleaf possono essere utilizzati per regolare la logica se si desidera sommare solo le foglie, o tutti i nodi.
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