Skip to content
Advertisement

Cumulative calculation using SQL

I have a supply and a demand table as shown below,

Supply
Center1  1-Sep  500
Center1  1-Dec  1000

Demand
Center1  Req-1  1-Aug   300
Center1  Req-2  15-Aug  250
Center1  Req-3  1-Sep   1100

write a SQL code to get the output as shown below,

Expected output,

                 Req_Dt   Supply_Dt    Units supplied
Center1  Req-1   1-Aug      1-Sep       300
Center1  Req-2   15-Aug     1-Sep       200
Center1  Req-2   15-Aug     1-Dec       50
Center1  Req-3   1-Sep      1-Dec       950
Center1  Req-3   1-Sep      NULL        150

For a center1, if there is request-1 which came in asking for 300 units on 1-Aug but there is no supply available as of 1-Aug, so we cant fulfill the request until we get a supply. Next, supply received only on 1-Sep with 500 units. So, for the request-1, supply will happen on 1-Sep and 300 units will be supplied, so 200 is still remaining.

Now, the next request-2 on 15-Aug requires 250 units and since still 1-Sep can fulfill another 200 units of supply left over after using for req-1 we split the fulfillment for request by stating Req-2 can be fulfilled by 200 units on 1-Sep and remaining 50 only on 1-Dec when the next supply comes and so on.

Tried using while loop in Stored procedure but it takes more time when processing millions of rows. so either a function or sql needs to be written

Expected output,

Center   Request    Req_Dt    Supply_Dt    Units supplied
Center1  Req-1      1-Aug      1-Sep       300
Center1  Req-2      15-Aug     1-Sep       200
Center1  Req-2      15-Aug     1-Dec       50
Center1  Req-3      1-Sep      1-Dec       950
Center1  Req-3      1-Sep      NULL        150

Advertisement

Answer

Here is one way of looping over the data to get the desired outcome.

Essentially I have built a series of handling tables. One for filtering each demand and supply row at a time and one for actually manipulating the counts of things supplied/in demand rows, this is done so the data itself can be left alone. Finally there is a table created on the fly to generate the output itself.

The loop basically continues until there is nothing left to filter for and if anything is still outstanding either from a supply row or a demand row then that is also interted in to the output table. On each pass the current requirement and supplier handler tables are queried to see if something new needs adding, then at the end of the loop after the output row has been generated anything which is at 0 or less is removed from the handler tables to ensure only one supply or demand row is being dealt with at a time.

EDIT: I’ve added a loop for the center. This means that a data set with multiple centers will now run successfully.

I’ve also tested the example with more rows. With 48 supply records and 80 demand records this query produces 128 transaction rows in one second so I suspect the performance issues you were noticing may have had to do with the center looping rather than the query performance itself. Let me know how you get on with the updated query and if there are performance issues we can look in to why that is.

Please see example query and data using the Center1 and Center 2 data provided from your comment and output result set pasted below query.

declare @Supply table (
    Center nvarchar(20),
    [Date] nvarchar(10),
    Supply int
);

declare @Demand table (
    Center nvarchar(20),
    Requirement nvarchar(10),
    [Date] nvarchar(10),
    Demand int
);

insert @Supply (Center, Date, Supply)
values
('Center1', '1-Sep', 500),
('Center1', '1-Dec', 1000),
('Center2', '1-Oct', 700);


insert @Demand (Center, Requirement, Date, Demand)
values
('Center1', 'Req-1', '1-Aug', 300),
('Center1', 'Req-2', '15-Aug', 250),
('Center1', 'Req-3', '1-Sep', 1100),
('Center2', 'Req-1', '1-Sep', 500), 
('Center2', 'Req-2', '1-Oct', 250);

declare @output table
( Center nvarchar(20),
  Requirement nvarchar(10),
  Req_Dt nvarchar(10),
  Supply_Dt nvarchar(10),
  Units_supplied int
);

declare @reqfilter table (
    Requirement nvarchar(10),
    Center nvarchar(20)
);

declare @supfilter table (
    Date nvarchar(10),
    Center nvarchar(20)
);

declare @req table (
    Center nvarchar(20),
    Requirement nvarchar(10),
    Date nvarchar(10),
    Demand int
);

declare @sup table (
    Center nvarchar(20),
    Date nvarchar(10),
    Supply int
);

Declare @Centerfilter table (Center nvarchar(20));

insert @Centerfilter
select distinct Center from @Supply
union
select distinct Center from @Demand;

--select count(*) from @Supply as supply
--select count(*) from @Demand as demand

while exists (select 1 from @Centerfilter)
begin
    insert @reqfilter 
    select requirement, c.Center from @Demand d inner join 
    (select top 1 Center from @Centerfilter order by Center) c
    on d.Center=c.Center;

    insert @supfilter
    select date, s.Center from @Supply s inner join
    (select top 1 Center from @Centerfilter order by Center) c
    on s.Center=c.Center;

    while exists (select 1 from @reqfilter outer apply @supfilter)
    begin 
        if not exists (select 1 from @req)
        begin
                insert @req
                select top 1 d.Center, d.Requirement, date, demand from
                @Demand d inner join @reqfilter r on d.center=r.Center where d.Requirement in (select top 1 Requirement from @reqfilter order by Center, Requirement)
                order by Center, parse(date as date);
                delete @reqfilter from @reqfilter rq inner join @req r on rq.Requirement=r.Requirement and rq.Center=r.Center;
        end
        if not exists (select 1 from @sup)
        begin
            insert @sup
            select top 1 s.Center, date, supply from
            @Supply s inner join @Centerfilter c on s.Center=c.Center where date in (select top 1 Date from @supfilter order by Center, parse(date as date))
            order by center, parse(date as date);
            delete @supfilter from @supfilter s inner join @sup su on s.Date=su.date and s.Center=su.Center;
        end
        insert @output
        select coalesce(d.center, s.center), requirement, d.date, s.date, case when supply>Demand then demand else supply end
        from @req d inner join @sup s on d.Center=s.Center

        update @req set demand=demand-o.Units_supplied 
        from @req r inner join (select top 1 Center, Requirement, Units_supplied from @output order by Center desc, parse(Req_Dt as date) desc, parse(Supply_Dt as date) desc) o on r.Center=o.Center and r.Requirement=o.Requirement;

        update @sup set Supply=supply-units_supplied
        from @sup s inner join @req r on s.Center=r.Center 
        inner join (select top 1 center, requirement, units_supplied from @output order by Center desc, parse(Req_Dt as date) desc, parse(Supply_Dt as date) desc) 
        o on s.Center=o.Center and r.Requirement=o.Requirement;
        delete from @req where Demand<=0;
        delete from @sup where Supply<=0;
    end

    if exists (select 1 from @req)
    begin
        insert @output
        select center, requirement, Date, null, demand from @req;
    end

    if exists (select 1 from @sup)
    begin
        insert @output
        select center, null, null, date, supply from @sup;
    end
    delete @Centerfilter where Center in (select top 1 Center from @Centerfilter order by Center);
    delete from @reqfilter;
    delete from @supfilter;
    delete from @req;
    delete from @sup;
end

    select * from @output;

enter image description here

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement