Search code examples
sqlsql-serversql-server-2008common-table-expressionbucket

Bucket Filling SQL query CTE


I want to achieve the below output from the given input tables.

Input Table (Bucket to be filled)

ID | FullCapacity | CurrentAmount    
---+--------------+--------------
B1 |     100      |     0    
B2 |      50      |     0
B3 |      70      |     0

Input Table (Filler Table)

        ID | Filler            
        ---+-------
        F1 | 90              
        F2 | 70          
        F3 | 40    
        F4 | 20 

Output table should have below showing filling process.

ID | FullCapacity | CurrentAmount       
---+--------------+--------------
B1 |    100       |    90        
B2 |     50       |     0    
B3 |     70       |     0
---+--------------+--------------
B1 |    100       |   100        
B2 |     50       |    50
B3 |     70       |    10
---+--------------+--------------
B1 |    100       |   100      
B2 |     50       |    50    
B3 |     70       |    50
---+--------------+--------------
B1 |    100       |   100        
B2 |     50       |    50    
B3 |     70       |    70

I am trying to fill this one by one from filler to bucket. Can we do this without using cursor?

Please see that we can have multiple types of buckets for example red bucket, blue bucket and red filler, blue filler. Red filler to go to red bucket, blue filler to blue and so on.

Thank you


Solution

  • You can do this in SQL Server 2008 like this:

    declare @Buckets table (ID char(2), FullCapacity int)
    declare @Filler table (ID char(2), Filler int)
    
    insert into @Buckets 
    select 'B1', 100 union all
    select 'B2', 50 union all
    select 'B3', 70 
    
    insert into @Filler 
    select 'F1', 90 union all
    select 'F2', 70 union all
    select 'F3', 40 union all
    select 'F4', 20
    
    
    select 
        b.ID, 
        b.FullCapacity,
        case 
            when f.TotalFill < b.RunningTotalCapacity then 0
            when f.TotalFill > b.RunningTotalCapacity + b.FullCapacity then b.FullCapacity
            else f.TotalFill - b.RunningTotalCapacity
        end as CurrentAmount
    from
    (
        select      
        ID,
        Filler,
        (
            select sum(f2.Filler)
            from @Filler as f2
            where f2.ID <= f.ID
        ) as TotalFill
        from @Filler as f
    ) as f
    cross join 
    (
        select 
            ID,
            FullCapacity, 
            (
                select isnull(sum(b2.FullCapacity), 0)
                from @Buckets as b2
                where b2.ID < b.ID
            ) as RunningTotalCapacity
        from @Buckets as b
    ) as b
    order by f.ID, b.ID
    

    You can do this using windowing functions like this:

    SQL Server 2012+

        declare @Buckets table (ID char(2), FullCapacity int)
        declare @Filler table (ID char(2), Filler int)
    
        insert into @Buckets values
        ('B1', 100),
        ('B2', 50),
        ('B3', 70)
    
        insert into @Filler values
        ('F1', 90),
        ('F2', 70),
        ('F3', 40),
        ('F4', 20)
    
        ;with fillerCte as
        (
            select      
                ID,
                Filler,
                sum(Filler) over (order by ID) as TotalFill
            from @Filler
        ), 
        BucketCte as
        (
            select 
                ID,
                FullCapacity,
                sum(FullCapacity) over (order by ID) - FullCapacity as RunningTotalCapacity
            from @Buckets
        )
        select 
            b.ID, 
            b.FullCapacity,
            case 
                when f.TotalFill < b.RunningTotalCapacity then 0
                when f.TotalFill > b.RunningTotalCapacity + b.FullCapacity then b.FullCapacity
                else f.TotalFill - b.RunningTotalCapacity
            end as CurrentAmount
        from fillerCte as f
        cross join BucketCte as b
        order by f.ID, b.ID