2015-07-15 33 views
8

Załóżmy, że mam 2 tabele T1 i T2 następującoOracle funkcja agregacja przeznaczyć kwotę

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 

Każdy rekord w tabeli T1 reprezentuje torbę i jego pojemność, tutaj mam 5 torebek. Chcę napisać SQL, które przeznaczyć przedmioty na stole T2 do każdej torby z tego samego rodzaju, tj wynik powinien być jak ten

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 

Dlatego jestem znalezienie jakiegoś funkcji agregacji, nazwijmy to allocate() , który może wytworzyć kolumnę allocated_amount jak wyżej. mam przypuszczenie, że jeśli istnieje, to może być używane jak to

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 

Mój obecny rozwiązaniem jest użycie temp tabeli i PL/pętli SQL do obliczenia, ale mam nadzieję, że mogę to zrobić za pomocą jednej prostej SQL.

+1

jest kolejność przydziału odpowiedniego? W twoich przykładowych danych szwy przydzielasz na pojemność DESC, w funkcji agregacji, którą zamawiasz na ASC. A może szukacie optymalnej alokacji? –

+0

Tak, oczekiwałem, że zamówienie będzie "DESC". Poprawię wpis. – asinkxcoswt

Odpowiedz

4

Poszukujesz łącznej kwoty. Coś takiego:

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

literówka - "od t1) t dołącz" ==> "od t1) t1 dołącz" Niesamowite zapytanie! – OldProgrammer

3

zakładając alokacja w kolejności potomkiem pojemności worka

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 

Zauważ, że kolejność przydziału jest ważne, jeśli chcesz, aby zminimalizować pojemność odpadów i znalezienia optymalnej alokacji za pomocą różne zamówienia mogą być trudne.

+0

Przyjemne rozwiązanie, mam nadzieję, że mógłbym zaakceptować więcej głosów, ale wybieram rozwiązanie @ Gordona z powodu wprowadzenia do skumulowanej sumy. – asinkxcoswt

4

To powinno załatwić sprawę:

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