Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oracle aggregation function to allocate amount

Suppose I have 2 tables T1 and T2 as follows

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

Each record in table T1 represents a bag and its capacity, here I have 5 bags. I want to write an SQL that allocate items in table T2 into each bag with the same type, i.e. the result should be like this

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

Therefore, I am finding some kind of aggregation function, let's call it allocate(), that can produce the column allocated_amount as above. I have a guess that, if exists, it might be used like this

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

My current solution is to use a temp table and PL/SQL loop for calculation, but I hope I can do it with one simple SQL.

like image 912
asinkxcoswt Avatar asked Jul 15 '15 15:07

asinkxcoswt


3 Answers

You are looking for a cumulative sum. Something like this:

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;
like image 166
Gordon Linoff Avatar answered Sep 21 '22 19:09

Gordon Linoff


This should do the trick:

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
like image 20
Sentinel Avatar answered Sep 20 '22 19:09

Sentinel


assuming allocation in the descendent order of bag capacity

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 

Note that the order of the allocation is important if you want to minimize the waste capacity and finding an optimal allocation using different orders could be hard.

like image 35
Marmite Bomber Avatar answered Sep 20 '22 19:09

Marmite Bomber