SQL Server 2008 R2 - 引用上一行的计算

时间:2015-12-23 03:53:04

标签: sql excel tsql sql-server-2008-r2 excel-formula

我希望设计一个查询,其中计算将单元格中的值与同一列中前一个单元格中的值进行比较,另一个计算使用先前的计算结果来引用先前的单元格进行计算。数据将按以下顺序从下面的示例图片 - Material,Plant和YearWeek

开始

Sample Data Image

从下图中,蓝色列标题表示原始数据,黄色列标题表示计算行。

计算行的公式如下:

  • 首先? = IF(AND(B2 = B1,C2 = C1)," No"," First")
  • 实际可用= IF(F2 ="第一",D2-E2,G1-E2)

从我的调查来看,LEAD和LAG功能可以帮助实现这一目标,但由于我受限于SQL Server 2008 R2,我无法使用它们。请查找查询以创建表格。

    CREATE TABLE ZDAYS ([YearWeek] VARCHAR(7), 
    [Material] VARCHAR(3), [Plant] VARCHAR(3),
    [Inventory] INT, [Demand] INT)

    INSERT INTO ZDAYS VALUES ('2015-42', 'ABC', '101', 20, 5)
    INSERT INTO ZDAYS VALUES ('2015-43', 'ABC', '101', 20, 3)
    INSERT INTO ZDAYS VALUES ('2015-44', 'ABC', '101', 20, 2)
    INSERT INTO ZDAYS VALUES ('2015-42', 'ABC', '201', 30, 10)
    INSERT INTO ZDAYS VALUES ('2015-43', 'ABC', '201', 30, 8)
    INSERT INTO ZDAYS VALUES ('2015-44', 'ABC', '201', 30, 4)
    INSERT INTO ZDAYS VALUES ('2015-42', 'XYZ', '101', 10, 3)
    INSERT INTO ZDAYS VALUES ('2015-43', 'XYZ', '101', 10, 2)
    INSERT INTO ZDAYS VALUES ('2015-44', 'XYZ', '201', 20, 4)

最终数据输出

 YearWeek   Material    Plant   Inventory   Demand  First?  Actual Available
 2015-42    ABC          101        20        5     First         15             
 2015-43    ABC          101        20        3     No            12             
 2015-44    ABC          101        20        2     No            10
 2015-42    ABC          201        30        10    First         20
 2015-43    ABC          201        30        8     No            12
 2015-44    ABC          201        30        4     No            8
 2015-42    XYZ          101        10        3     First         7
 2015-43    XYZ          101        10        2     No            5
 2015-44    XYZ          201        20        4     First         16

2 个答案:

答案 0 :(得分:1)

改编自答案'无联合和无分析功能'于:

http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql-server-2012-analytic-function/

declare  @ZDAYS TABLE ([YearWeek] VARCHAR(7), 
    [Material] VARCHAR(3), [Plant] VARCHAR(3),
    [Inventory] INT, [Demand] INT)

    INSERT INTO @ZDAYS VALUES ('2015-42', 'ABC', '101', 20, 5)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'ABC', '101', 20, 3)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'ABC', '101', 20, 2)
    INSERT INTO @ZDAYS VALUES ('2015-42', 'ABC', '201', 30, 10)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'ABC', '201', 30, 8)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'ABC', '201', 30, 4)
    INSERT INTO @ZDAYS VALUES ('2015-42', 'XYZ', '101', 10, 3)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'XYZ', '101', 10, 2)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'XYZ', '201', 20, 4);

WITH T1 (RowNum, UniqueID, YearWeek, Material, Plant, Inventory, Demand)
AS
    (SELECT Row_Number() OVER(ORDER BY z.Material,z.Plant,z.YearWeek ) N
        ,1.0 + floor(10000 * RAND(convert(varbinary, newid()))) as UniqueID 
        ,z.YearWeek
        ,z.Material
        ,z.Plant
        ,z.Inventory
        ,z.Demand
        FROM @ZDAYS z
    )
select T1.*,
CASE WHEN RowNum%2=1 THEN MAX(CASE WHEN RowNum%2=0 THEN UniqueID END) OVER (Partition BY (RowNum+1)/2) ELSE MAX(CASE WHEN RowNum%2=1 THEN UniqueID END) OVER (Partition BY RowNum/2) END LeadValUniqueID,
CASE WHEN RowNum%2=1 THEN MAX(CASE WHEN RowNum%2=0 THEN UniqueID END) OVER (Partition BY RowNum/2) ELSE MAX(CASE WHEN RowNum%2=1 THEN UniqueID END) OVER (Partition BY (RowNum+1)/2) END LagValUniqueID
FROM T1
ORDER BY T1.Material, T1.Plant, T1.YearWeek
GO

或者,与Lead()和Lag()行相同的逻辑用于标识而不是生成唯一ID。

declare  @ZDAYS TABLE ([YearWeek] VARCHAR(7), 
    [Material] VARCHAR(3), [Plant] VARCHAR(3),
    [Inventory] INT, [Demand] INT)

    INSERT INTO @ZDAYS VALUES ('2015-42', 'ABC', '101', 20, 5)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'ABC', '101', 20, 3)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'ABC', '101', 20, 2)
    INSERT INTO @ZDAYS VALUES ('2015-42', 'ABC', '201', 30, 10)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'ABC', '201', 30, 8)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'ABC', '201', 30, 4)
    INSERT INTO @ZDAYS VALUES ('2015-42', 'XYZ', '101', 10, 3)
    INSERT INTO @ZDAYS VALUES ('2015-43', 'XYZ', '101', 10, 2)
    INSERT INTO @ZDAYS VALUES ('2015-44', 'XYZ', '201', 20, 4);

-- LAG and LEAD result rows concatenated
WITH T1 (RowNum,  YearWeek, Material, Plant, Inventory, Demand)
AS
    (SELECT Row_Number() OVER(ORDER BY z.Material,z.Plant,z.YearWeek ) N
        ,z.YearWeek
        ,z.Material
        ,z.Plant
        ,z.Inventory
        ,z.Demand
        FROM @ZDAYS z
    )
select T1.*,
CASE WHEN RowNum%2=1 THEN MAX(CASE WHEN RowNum%2=0 THEN YearWeek + ' ' + Material + ' ' + Plant + ' ' + cast(Inventory as varchar(5)) + ' ' + cast(Demand as varchar(5)) END) OVER (Partition BY (RowNum+1)/2) ELSE MAX(CASE WHEN RowNum%2=1 THEN YearWeek + ' ' + Material + ' ' + Plant + ' ' + cast(Inventory as varchar(5)) + ' ' + cast(Demand as varchar(5)) END) OVER (Partition BY RowNum/2) END LeadRowValues,
CASE WHEN RowNum%2=1 THEN MAX(CASE WHEN RowNum%2=0 THEN YearWeek + ' ' + Material + ' ' + Plant + ' ' + cast(Inventory as varchar(5)) + ' ' + cast(Demand as varchar(5)) END) OVER (Partition BY RowNum/2) ELSE MAX(CASE WHEN RowNum%2=1 THEN YearWeek + ' ' + Material + ' ' + Plant + ' ' + cast(Inventory as varchar(5)) + ' ' + cast(Demand as varchar(5)) END) OVER (Partition BY (RowNum+1)/2) END LagRowValues
FROM T1
ORDER BY T1.Material, T1.Plant, T1.YearWeek
GO

答案 1 :(得分:0)

在早期版本的SQL Server中,您可以使用apply

select z.yearweek, z.material, z.plant, z.inventory, z.demand,
       (case when seqnum = 1 then 'Yes' else 'No' end) as isFirst,
       (inventory - cume.demand) as ActualAvailable
from (select z.*,
             row_number() over (partition by material, plant order by yearweek) as seqnum
      from zdays z
     ) z outer apply
     (select sum(z2.demand) as demand
      from zdays z2
      where z2.material = z.material and z2.plant = z.plant and
            z2.yearweek <= z.yearweek
     ) cume