2015-07-15 20 views
8

Supponiamo Ho 2 tabelle T1 e T2 come seguefunzione di aggregazione Oracle per ripartire il valore

T1:

bag_id bag_type capacity 
------|--------|-------- 
    1  A  500 
    2  A  300 
    3  A  100 
    4  B  200 
    5  B  100 

T2:

item_type item_amount 
---------|----------- 
    A   850 
    B   300 

Ogni record nella tabella T1 rappresenta un sacchetto e la sua capacità, qui ho 5 borse. Voglio scrivere uno SQL che allocare elementi nella tabella T2 in ogni sacchetto con lo stesso tipo, vale a dire il risultato dovrebbe essere simile a questo

bag_id bag_type capacity allocated_amount 
------|--------|--------|---------------- 
    1  A  500  500 
    2  A  300  300 
    3  A  100  50 
    4  B  200  200 
    5  B  100  100 

Pertanto, mi sto trovando una sorta di funzione di aggregazione, chiamiamolo allocate() , che può produrre la colonna allocated_amount come sopra. Ho una supposizione che, se esiste, potrebbe essere utilizzato come questo

select 
    t1.bag_id, 
    t1.bag_type, 
    t1.capacity, 
    allocate(t2.item_amount, t1.capacity) 
     over (partition by t1.bag_type order by t1.capacity desc) as allocatd_amount 
from t1, t2 
where t2.item_type = t1.bag_type 

mia soluzione attuale è quella di utilizzare una tabella temporanea e PL loop/SQL per il calcolo, ma spero di poter farlo con un semplice SQL.

+1

è ordine della dotazione rilevante? Nei tuoi dati di esempio si giunge a allocare sulla capacità DESC, nella funzione di aggregazione ordinata sulla capacità ASC. Oppure il tuo evento cerca un'allocazione ottimale? –

+0

Sì, mi aspettavo che l'ordine fosse 'DESC'. Correggerò il post – asinkxcoswt

risposta

4

Stai cercando una somma cumulativa. Qualcosa di simile a questo:

select t1.*, 
     (case when cumecap <= t2.item_amount 
      then t1.capacity 
      when cumecap - t1.capacity <= t2.item_amount 
      then t2.item_amount - (cumecap - t1.capacity) 
      else 0 
     end) as allocated_capacity 
from (select t1.*, 
      sum(t1.capacity) over (partition by bag_type order by bag_id) as cumecap 
     from t1 
    ) t1 join 
    t2 
    on t1.bag_type = t2.item_type; 
+0

typo - "da t1) t join" ==> "da t1) t1 join" Query impressionante! – OldProgrammer

3

assumendo l'assegnazione in ordine discendente di capacità del sacco

with agg as ( 
select bag.BAG_ID, bag.BAG_TYPE, bag.CAPACITY, 
SUM(bag.CAPACITY) over (partition by bag.bag_type order by bag.capacity DESC) agg_capacity, 
item_amount 
from bag, item 
where bag.bag_type = item.item_type 
) 
select 
    BAG_ID, BAG_TYPE, CAPACITY, 
    case when ITEM_AMOUNT >= AGG_CAPACITY then CAPACITY /* Full allocated */ 
    when ITEM_AMOUNT >= AGG_CAPACITY-CAPACITY then ITEM_AMOUNT - (AGG_CAPACITY-CAPACITY) /* partly allocated */ 
    else 0 end /* not allocated */ 
    as allocated 
from agg 
order by bag_type, capacity desc; 

    BAG_ID BAG_TYPE CAPACITY ALLOCATED 
    ------ -------- ---------- ---------- 
    1 A    500  500 
    2 A    300  300 
    3 A    100   50 
    4 B    200  200 
    5 B    100  100 

Nota che l'ordine della dotazione è importante se si vuole ridurre al minimo la capacità di rifiuti e trovare un'allocazione ottimale utilizzando ordini diversi potrebbero essere difficili.

+0

Bella soluzione, spero di poter accettare più voti, ma scelgo la soluzione di @ Gordon a causa dell'introduzione alla somma cumulativa. – asinkxcoswt

4

Questo dovrebbe fare il trucco:

select t1.bag_id 
    , t1.bag_type 
    , t1.capacity 
    , least(t1.capacity -- don't over fill the bag 
      , greatest(0 -- don't under fill the bag 
         , t2.item_amount -- to be allocated 
         - nvl(sum(t1.capacity) -- less previous allocations 
          over (partition by t1.bag_type 
            order by t1.capacity desc 
          rows between unbounded preceding and 1 preceding) 
          , 0))) Allocated 
    from t1 
    join t2 
    on t2.item_type = t1.bag_type; 

    BAG_ID B CAPACITY ALLOCATED 
---------- - ---------- ---------- 
     1 A  500  500 
     2 A  300  300 
     3 A  100   50 
     4 B  200  200 
     5 B  100  100