使用SQL的累积计算

时间:2019-06-06 01:50:50

标签: sql sql-server

我有一个供求表,如下所示,

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

编写SQL代码以获取输出,如下所示,

预期的输出

                 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

对于中心1,如果有请求1要求300个 1月8日,但自8月1日起没有可用的货源,因此直到获得货源,我们才能满足要求。接下来,仅在500个单位的9月1日收到货。因此,对于请求1,供应将在1月9日发生,并且将提供300个单位,因此仍然剩余200个。

现在,在8月15日的下一个请求2需要250个单位,并且由于在使用req-1之后,仍然需要9个1-Sep来满足剩余的200个单位的供应,因此我们通过陈述Req-2可以对请求的实现进行拆分在下一次供应到来时,在9月1日满足200个单位的需求,而在下一个供应期至12月1日仅满足50个需求。

尝试在存储过程中使用while循环,但处理数百万行时会花费更多时间。因此,需要编写函数或sql

预期的输出

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

2 个答案:

答案 0 :(得分:1)

这是循环遍历数据以获得所需结果的一种方法。

基本上,我已经建立了一系列处理表。一种用于一次过滤每个需求和供应行,另一种用于实际处理供应/需求行中事物的计数,这样做是为了使数据本身可以单独放置。最后,有一个动态创建的表可以自己生成输出。

循环基本上会继续进行,直到没有任何要过滤的内容为止,如果供应行或需求行中仍有任何未解决的内容,则该内容也将插入输出表中。每次通过时,都会查询当前需求和供应商处理程序表以查看是否需要添加新内容,然后在生成输出行后的循环结束时从处理程序表中删除等于或小于0的任何内容以确保仅一次处理一个供求行。

编辑: 我为中心添加了一个循环。这意味着具有多个中心的数据集现在将成功运行。

我还用更多行测试了该示例。有48个供应记录和80个需求记录,此查询在一秒钟内产生128个事务行,因此我怀疑您注意到的性能问题可能与中心循环有关,而不是查询性能本身。 让我知道您如何处理更新后的查询,如果有性能问题,我们可以调查原因。

请使用注释中提供的Center1和Center 2数据查看示例查询和数据,并将输出结果集粘贴到查询下方。

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

答案 1 :(得分:1)

DROP TABLE IF EXISTS #Supply
DROP TABLE IF EXISTS #Demand

SELECT * INTO #Supply FROM
(
SELECT CAST('09-01-2019' AS Date) AS dt, 'DC1' as DC, 500 AS KW
UNION
SELECT CAST('12-01-2019' AS Date), 'DC1', 1000
UNION
select CAST('10-01-2019' AS Date), 'DC2', 700
UNION
select CAST('10-01-2019' AS Date), 'DC3', 300
) Supply


SELECT * INTO #demand FROM 
(
SELECT 'DC1' as DC, 'D1' as DemandId, 300 as KW, CAST('01-08-2019' AS Date) AS dt
UNION
SELECT 'DC1', 'D2', 250, CAST('08-15-2019' AS Date)
UNION
SELECT 'DC1', 'D3', 1100, CAST('08-15-2019' AS Date)
UNION
SELECT 'DC2', 'D1', 500, CAST('09-01-2019' AS Date)
UNION
SELECT 'DC2', 'D2', 250, CAST('09-15-2019' AS Date)
UNION
SELECT 'DC3', 'D1', 100, CAST('10-01-2019' AS Date)
UNION
SELECT 'DC3', 'D2', 200, CAST('11-01-2019' AS Date)
) Demand

DROP TABLE IF EXISTS #AllSupply
DROP TABLE IF EXISTS #AllDemand
DROP TABLE IF EXISTS #Final


SELECT RowID, DT, DC, KW, RunningTotalCurrent,
LAG(RunningTotalCurrent, 1,0) OVER(partition by DC Order by RowId) AS PriorRunning, 
LEAD(RunningTotalCurrent, 1,0) OVER(partition by DC Order by RowId)  AS NextRunning
into #AllSupply
FROM (SELECT S1.RowID, S1.dt, s1.KW, S1.DC , SUM(S1.KW) over (PARTITION BY s1.dc ORDER BY s1.dt rows UNBOUNDED preceding) AS RunningTotalCurrent 
FROM (SELECT *, ROW_NUMBER() OVER(Partition By DC Order By dt ) AS RowID FROM #Supply) S1
) t


SELECT RowID, DemandId, DT, DC, KW, RunningTotalCurrent,LAG(RunningTotalCurrent, 1,0) OVER(partition by DC Order by RowId) AS PriorRunning, 
LEAD(RunningTotalCurrent, 1,0) OVER( partition by DC Order by RowId) AS NextRunning
into #AllDemand
FROM (SELECT d1.RowID, d1.dt, d1.KW, d1.DC , d1.demandId, SUM(d1.KW) over (PARTITION BY d1.dc ORDER BY d1.dt rows UNBOUNDED preceding) AS RunningTotalCurrent 
FROM (SELECT *, ROW_NUMBER() OVER(Partition By DC Order By dt, DemandId ) AS RowID FROM #Demand) d1) t


SELECT s.DC, D.DemandId, D.dt AS DemandDate, CASE WHEN s.dt < d.dt THEN d.dt ELSE ISNULL(S.dt,'11/11/2222') END AS SupplyDate, s.RunningTotalCurrent as SupplyRunningTotal,
 d.RunningTotalCurrent as DemandRunningTotal, s.PriorRunning as SupplyPreviousTotal, d.PriorRunning as DemandPreviousTotal, s.kw as SupplyKW, d.kw as DemandKW, 
CASE WHEN S.RunningTotalCurrent >= d.RunningTotalCurrent and s.PriorRunning = d.PriorRunning THEN d.kw
WHEN S.RunningTotalCurrent >= d.RunningTotalCurrent and s.PriorRunning > d.PriorRunning THEN d.kw - (s.PriorRunning - d.PriorRunning)
WHEN S.RunningTotalCurrent < d.RunningTotalCurrent AND s.PriorRunning = d.PriorRunning THEN s.kw 
WHEN S.RunningTotalCurrent < d.RunningTotalCurrent AND s.PriorRunning > d.PriorRunning THEN d.kw - (s.PriorRunning - d.PriorRunning)
WHEN S.RunningTotalCurrent < d.RunningTotalCurrent AND s.PriorRunning < d.PriorRunning THEN S.RunningTotalCurrent - d.PriorRunning 
WHEN S.RunningTotalCurrent = d.RunningTotalCurrent THEN d.kw 
END  AS Supply 
INTO #Final
FROM #AllDemand D
LEFT JOIN #AllSupply S ON S.DC = D.DC
AND D.PriorRunning <= S.RunningTotalCurrent 
AND D.RunningTotalCurrent >= S.PriorRunning


SELECT DC, DemandId, DemandDate, SupplyDate, Supply 
FROM #Final
UNION 
SELECT d.DC, d.DemandId, d.dt, '11/11/2222', d.kw - a.Supply
FROM (SELECT Dc, DemandId, SUM(Supply) AS Supply FROM #Final GROUP BY DC, DemandId) a 
JOIN #demand d on a.DC= d.DC and a.DemandId = d.DemandId and d.kw > a.Supply