创建一列,根据先进先出计算每批货物的余额部分

Create a column to calculate the portion of balance for every shipment according to FIFO

我有一个查询要获取具有余额的库存项目及其接收交易代码及其按日期订购的数量,我想创建一个列,该列仅包含每个 RS 代码的余额比例先出原则。作为附加示例中的示例

我们有 StockItemId = (2222,2262,2263).

预期结果将是: 如图所示,RS_Portion 中每一行的余额取决于代码的数量,每个项目的 RS_Portion 的总和应等于同一项目的 GlobalBalance。

这是我的代码:

WITH M AS (

            SELECT WHS.StockItemId,SUM(WHS.OnHandBalance) AS GlobalBalance
            FROM Warehouse.WarehouseStocks WHS 
            GROUP BY WHS.StockItemId
            HAVING SUM(WHS.OnHandBalance) > 0 
            )
SELECT M.StockItemId,M.GlobalBalance,WHWorkOrderHeader.Code,WHWorkOrderDetails.Quantity,WHWorkOrderHeader.Date
FROM M
LEFT OUTER JOIN Warehouse.WHWorkOrderDetails
ON WHWorkOrderDetails.StockItemId = M.StockItemId
LEFT OUTER JOIN  Warehouse.WHWorkOrderHeader
ON  WHWorkOrderHeader.ID = WHWorkOrderDetails.WHWorkOrderHeaderId
WHERE WHWorkOrderHeader.Type = 'RS' AND M.StockItemId IN (2222,2262,2263)
ORDER BY M.StockItemId ASC, WHWorkOrderHeader.Date DESC

StockItemId,GlobalBalance,Code,Quantity,Date
2222,158.0000,RS-1-1543,1,2017-12-13 07:25:29.727
2222,158.0000,RS-1-1471,77,2017-08-22 14:53:11.880
2222,158.0000,RS-1-1470,77,2017-08-22 14:53:09.920
2222,158.0000,RS-1-1409,5,2017-02-16 13:41:00.740
2222,158.0000,RS-1-1409,5,2017-02-16 13:41:00.740
2222,158.0000,RS-1-1231,150,2015-09-29 15:41:45.000
2222,158.0000,RS-1-1226,100,2015-09-21 09:50:37.000
2262,23.0000,RS-14-371,20,2016-10-16 09:11:57.670
2262,23.0000,RS-14-334,30,2016-08-04 16:16:48.803
2262,23.0000,RS-14-303,18,2016-03-08 13:17:17.023
2262,23.0000,RS-14-301,70,2016-03-01 13:45:49.767
2262,23.0000,RS-14-298,30,2016-02-18 10:10:03.973
2262,23.0000,RS-14-286,2,2016-02-08 10:18:14.203
2262,23.0000,RS-14-285,30,2016-02-07 07:14:01.000
2262,23.0000,RS-14-280,3,2016-02-02 15:11:12.220
2262,23.0000,RS-14-276,1,2016-01-18 12:13:37.860
2262,23.0000,RS-14-274,2,2016-01-14 14:33:53.863
2262,23.0000,RS-14-273,1,2016-01-14 13:25:20.457
2262,23.0000,RS-14-271,1,2016-01-12 16:43:30.397
2262,23.0000,RS-14-270,4,2016-01-12 15:54:43.380
2262,23.0000,RS-14-268,1,2016-01-11 16:43:36.843
2262,23.0000,RS-14-267,1,2016-01-10 13:19:42.617
2262,23.0000,RS-14-266,1,2016-01-06 15:58:00.513
2262,23.0000,RS-14-261,1,2016-01-03 15:20:07.410
2262,23.0000,RS-14-259,6,2015-12-30 13:58:46.217
2262,23.0000,RS-14-258,1,2015-12-30 10:59:23.120
2262,23.0000,RS-14-250,3,2015-12-17 16:32:29.937
2262,23.0000,RS-14-245,1,2015-12-10 14:19:14.910
2262,23.0000,RS-14-240,1,2015-12-06 13:13:45.847
2262,23.0000,RS-14-236,1,2015-11-30 15:36:41.233
2262,23.0000,RS-14-233,4,2015-11-26 12:44:22.067
2262,23.0000,RS-14-228,1,2015-11-23 11:38:35.553
2262,23.0000,RS-14-226,1,2015-11-23 10:11:49.393
2262,23.0000,RS-14-223,2,2015-11-10 13:04:17.540
2263,25.0000,RS-14-301,60,2016-03-01 13:45:49.767
2263,25.0000,RS-14-298,20,2016-02-18 10:10:03.973
2263,25.0000,RS-14-295,1,2016-02-11 17:04:54.423
2263,25.0000,RS-14-294,1,2016-02-10 16:06:13.090
2263,25.0000,RS-14-293,2,2016-02-10 15:58:40.353
2263,25.0000,RS-14-276,1,2016-01-18 12:13:37.860
2263,25.0000,RS-14-274,2,2016-01-14 14:33:53.863
2263,25.0000,RS-14-271,1,2016-01-12 16:43:30.397
2263,25.0000,RS-14-268,1,2016-01-11 16:43:36.843
2263,25.0000,RS-14-267,1,2016-01-10 13:19:42.617
2263,25.0000,RS-14-266,1,2016-01-06 15:58:00.513
2263,25.0000,RS-14-259,6,2015-12-30 13:58:46.217
2263,25.0000,RS-14-258,1,2015-12-30 10:59:23.120
2263,25.0000,RS-14-250,3,2015-12-17 16:32:29.937
2263,25.0000,RS-14-240,1,2015-12-06 13:13:45.847
2263,25.0000,RS-14-236,1,2015-11-30 15:36:41.233
2263,25.0000,RS-14-223,2,2015-11-10 13:04:17.540

假设您使用的是 SQL Server 2012 及更高版本,您可以使用 FIRST_VALUESUM() OVER (PARTITION BY)

我不太明白你为什么忽略代码 RS-1-1231RS-1-1226,它们在代码 RS-1-1409

之前

更新!!

Demo

DECLARE @Table TABLE (StockItemId int , GlobalBalance money,Code nvarchar(50), Quantity  INT , [Date] DATETIME)
  INSERT INTO @Table
  VALUES 
  ('2222', '158', 'RS-1-1543', '1', '2017-12-13 07:25:29.727')
    ,('2222', '158', 'RS-1-1471', '77', '2017-08-22 14:53:11.880')
    ,('2222', '158', 'RS-1-1470', '77', '2017-08-22 14:53:09.920')
    ,('2222', '158', 'RS-1-1409', '5', '2017-02-16 13:41:00.740')
    ,('2222', '158', 'RS-1-1409', '5', '2017-02-16 13:41:00.740')
    ,('2222', '158', 'RS-1-1231', '150', '2015-09-29 15:41:45.000')
    ,('2222', '158', 'RS-1-1226', '100', '2015-09-21 09:50:37.000')
    ,('2262', '23', 'RS-14-371', '20', '2016-10-16 09:11:57.670')
    ,('2262', '23', 'RS-14-334', '30', '2016-08-04 16:16:48.803')
    ,('2262', '23', 'RS-14-303', '18', '2016-03-08 13:17:17.023')
    ,('2262', '23', 'RS-14-301', '70', '2016-03-01 13:45:49.767')
    ,('2262', '23', 'RS-14-298', '30', '2016-02-18 10:10:03.973')
    ,('2262', '23', 'RS-14-286', '2', '2016-02-08 10:18:14.203')
    ,('2262', '23', 'RS-14-285', '30', '2016-02-07 07:14:01.000')
    ,('2262', '23', 'RS-14-280', '3', '2016-02-02 15:11:12.220')
    ,('2262', '23', 'RS-14-276', '1', '2016-01-18 12:13:37.860')
    ,('2262', '23', 'RS-14-274', '2', '2016-01-14 14:33:53.863')
    ,('2262', '23', 'RS-14-273', '1', '2016-01-14 13:25:20.457')
    ,('2262', '23', 'RS-14-271', '1', '2016-01-12 16:43:30.397')
    ,('2262', '23', 'RS-14-270', '4', '2016-01-12 15:54:43.380')
    ,('2262', '23', 'RS-14-268', '1', '2016-01-11 16:43:36.843')
    ,('2262', '23', 'RS-14-267', '1', '2016-01-10 13:19:42.617')
    ,('2262', '23', 'RS-14-266', '1', '2016-01-06 15:58:00.513')
    ,('2262', '23', 'RS-14-261', '1', '2016-01-03 15:20:07.410')
    ,('2262', '23', 'RS-14-259', '6', '2015-12-30 13:58:46.217')
    ,('2262', '23', 'RS-14-258', '1', '2015-12-30 10:59:23.120')
    ,('2262', '23', 'RS-14-250', '3', '2015-12-17 16:32:29.937')
    ,('2262', '23', 'RS-14-245', '1', '2015-12-10 14:19:14.910')
    ,('2262', '23', 'RS-14-240', '1', '2015-12-06 13:13:45.847')
    ,('2262', '23', 'RS-14-236', '1', '2015-11-30 15:36:41.233')
    ,('2262', '23', 'RS-14-233', '4', '2015-11-26 12:44:22.067')
    ,('2262', '23', 'RS-14-228', '1', '2015-11-23 11:38:35.553')
    ,('2262', '23', 'RS-14-226', '1', '2015-11-23 10:11:49.393')
    ,('2262', '23', 'RS-14-223', '2', '2015-11-10 13:04:17.540')
    ,('2263', '25', 'RS-14-301', '60', '2016-03-01 13:45:49.767')
    ,('2263', '25', 'RS-14-298', '20', '2016-02-18 10:10:03.973')
    ,('2263', '25', 'RS-14-295', '1', '2016-02-11 17:04:54.423')
    ,('2263', '25', 'RS-14-294', '1', '2016-02-10 16:06:13.090')
    ,('2263', '25', 'RS-14-293', '2', '2016-02-10 15:58:40.353')
    ,('2263', '25', 'RS-14-276', '1', '2016-01-18 12:13:37.860')
    ,('2263', '25', 'RS-14-274', '2', '2016-01-14 14:33:53.863')
    ,('2263', '25', 'RS-14-271', '1', '2016-01-12 16:43:30.397')
    ,('2263', '25', 'RS-14-268', '1', '2016-01-11 16:43:36.843')
    ,('2263', '25', 'RS-14-267', '1', '2016-01-10 13:19:42.617')
    ,('2263', '25', 'RS-14-266', '1', '2016-01-06 15:58:00.513')
    ,('2263', '25', 'RS-14-259', '6', '2015-12-30 13:58:46.217')
    ,('2263', '25', 'RS-14-258', '1', '2015-12-30 10:59:23.120')
    ,('2263', '25', 'RS-14-250', '3', '2015-12-17 16:32:29.937')
    ,('2263', '25', 'RS-14-240', '1', '2015-12-06 13:13:45.847')
    ,('2263', '25', 'RS-14-236', '1', '2015-11-30 15:36:41.233')
    ,('2263', '25', 'RS-14-223', '2', '2015-11-10 13:04:17.540')

;WITH Main as
(
    SELECT * , FIRST_VALUE (Quantity) OVER (PARTITION BY StockItemId ORDER BY [Date]) FirstQuantity,
            SUM(Quantity) OVER (PARTITION BY StockItemId) SumAllPerItem,
            GlobalBalance - SUM(Quantity) OVER (PARTITION BY StockItemId ORDER BY [Date] DESC ) DiffFromMOstRecent
    FROM @Table
)
,Results as
(
SELECT StockItemId , GlobalBalance , Code , Quantity , [Date], DiffFromMOstRecent , 
       ISNULL(LAG(DiffFromMOstRecent) OVER (PARTITION BY StockItemId ORDER BY [Date] DESC), DiffFromMOstRecent) PrevRunningSum
       ,CASE WHEN Quantity = FirstQuantity THEN GlobalBalance - SumAllPerItem + Quantity ELSE Quantity END RS_Portion
FROM Main

)

,Final as
(
SELECT  StockItemId , GlobalBalance , Code , Quantity , [Date]
,CASE WHEN MAX(PrevRunningSum) OVER(PARTITION BY StockItemId)  < 0 then Quantity + MAX(PrevRunningSum) OVER(PARTITION BY StockItemId) 
      WHEN DiffFromMOstRecent < 0 THEN PrevRunningSum
      ELSE RS_Portion END RS_Portion

FROM Results 
)
SELECT * 
FROM Final
WHERE RS_Portion >= 0 
ORDER BY StockItemId , [Date] Desc

由于您没有提供任何 DDL,我生成了示例数据,但它显然与您的相同。我希望您能够根据您的需要对其进行修改。试试这个:

declare @x table (stockItemId int, globalBalance int, quantity int, [date] date)
insert into @x values
(1,158, 1,'2018-03-31'),
(1,158, 77,'2018-03-30'),
(1,158, 77,'2018-03-29'),
(1,158, 5,'2018-03-28'),
(1,158, 5,'2018-03-27'),
(1,158, 150,'2018-03-26'),
(1,158, 100,'2018-03-25'),
(2,23, 20,'2018-03-31'),
(2,23, 77,'2018-03-30'),
(2,23, 77,'2018-03-29'),
(2,23, 5,'2018-03-28'),
(2,23, 5,'2018-03-27'),
(2,23, 150,'2018-03-26'),
(2,23, 100,'2018-03-25')


select stockItemId, globalBalance, quantity,
       case when cumsum < 0 then quantity + cumSum else quantity end [RS_Portion]
from (
    select stockItemId, globalBalance, quantity,
           globalBalance - SUM(quantity) over (partition by stockitemid order by [date] desc rows between unbounded preceding and current row) [cumSum]
    from @x
) a
--where [RS_Portion] > 0 - below is equivalent
where (case when cumsum < 0 then quantity + cumSum else quantity end) > 0

要理解此查询,您可能需要阅读 window 函数和 SQL 中使用 window 函数的累积总和。

这将为您提供您提供的输出:

WITH DataSource AS
(
    SELECT *
          ,[GlobalBalance] - SUM([Quantity]) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) AS [Dif]
          ,ROW_NUMBER() OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) [RecordID]
    FROM @DataSource
), DataSourceWithPrevDiff AS
(
    SELECT *
          ,LAG([Dif], 1, NULL) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) AS [PrevDif]
    FROM DataSource
)
SELECT [StockItemId]
      ,[GlobalBalance]
      ,[Date]
      ,[Code]
      ,[Quantity]
     ,CASE WHEN [Dif] > 0 THEN [Quantity] 
           WHEN [RecordID] = 1 THEN [Quantity] + [Dif]
           WHEN [PrevDif] > 0 THEN [PrevDif]
      END AS [RS_Portion]
FROM DataSourceWithPrevDiff
WHERE [Dif] > 0 
    OR [PrevDif] > 0
    OR [RecordID] = 1
ORDER BY [StockItemId]
        ,[Date] DESC;

当然,您可以按部分拆分查询。这个想法是计算每个 stoc 项目的当前行的总和 - 余额对。此外,我们还需要为每对获取第一项。然后在最终查询中,我们显示第一个项目,即到目前为止总和为正的项目,或者之前的总和为正的项目。

这是完整的工作示例:

DECLARE @DataSource TABLE
(
    [StockItemId] INT
   ,[GlobalBalance] INT
   ,[Code] VARCHAR(32)
   ,[Quantity] INT
   ,[Date] DATETIME2
);

INSERT INTO @DataSource ([StockItemId], [GlobalBalance], [Code], [Quantity], [Date])
VALUES   ('2222', '158', 'RS-1-1543', '1', '2017-12-13 07:25:29.727')
        ,('2222', '158', 'RS-1-1471', '77', '2017-08-22 14:53:11.880')
        ,('2222', '158', 'RS-1-1470', '77', '2017-08-22 14:53:09.920')
        ,('2222', '158', 'RS-1-1409', '5', '2017-02-16 13:41:00.740')
        ,('2222', '158', 'RS-1-1409', '5', '2017-02-16 13:41:00.740')
        ,('2222', '158', 'RS-1-1231', '150', '2015-09-29 15:41:45.000')
        ,('2222', '158', 'RS-1-1226', '100', '2015-09-21 09:50:37.000')
        ,('2262', '23', 'RS-14-371', '20', '2016-10-16 09:11:57.670')
        ,('2262', '23', 'RS-14-334', '30', '2016-08-04 16:16:48.803')
        ,('2262', '23', 'RS-14-303', '18', '2016-03-08 13:17:17.023')
        ,('2262', '23', 'RS-14-301', '70', '2016-03-01 13:45:49.767')
        ,('2262', '23', 'RS-14-298', '30', '2016-02-18 10:10:03.973')
        ,('2262', '23', 'RS-14-286', '2', '2016-02-08 10:18:14.203')
        ,('2262', '23', 'RS-14-285', '30', '2016-02-07 07:14:01.000')
        ,('2262', '23', 'RS-14-280', '3', '2016-02-02 15:11:12.220')
        ,('2262', '23', 'RS-14-276', '1', '2016-01-18 12:13:37.860')
        ,('2262', '23', 'RS-14-274', '2', '2016-01-14 14:33:53.863')
        ,('2262', '23', 'RS-14-273', '1', '2016-01-14 13:25:20.457')
        ,('2262', '23', 'RS-14-271', '1', '2016-01-12 16:43:30.397')
        ,('2262', '23', 'RS-14-270', '4', '2016-01-12 15:54:43.380')
        ,('2262', '23', 'RS-14-268', '1', '2016-01-11 16:43:36.843')
        ,('2262', '23', 'RS-14-267', '1', '2016-01-10 13:19:42.617')
        ,('2262', '23', 'RS-14-266', '1', '2016-01-06 15:58:00.513')
        ,('2262', '23', 'RS-14-261', '1', '2016-01-03 15:20:07.410')
        ,('2262', '23', 'RS-14-259', '6', '2015-12-30 13:58:46.217')
        ,('2262', '23', 'RS-14-258', '1', '2015-12-30 10:59:23.120')
        ,('2262', '23', 'RS-14-250', '3', '2015-12-17 16:32:29.937')
        ,('2262', '23', 'RS-14-245', '1', '2015-12-10 14:19:14.910')
        ,('2262', '23', 'RS-14-240', '1', '2015-12-06 13:13:45.847')
        ,('2262', '23', 'RS-14-236', '1', '2015-11-30 15:36:41.233')
        ,('2262', '23', 'RS-14-233', '4', '2015-11-26 12:44:22.067')
        ,('2262', '23', 'RS-14-228', '1', '2015-11-23 11:38:35.553')
        ,('2262', '23', 'RS-14-226', '1', '2015-11-23 10:11:49.393')
        ,('2262', '23', 'RS-14-223', '2', '2015-11-10 13:04:17.540')
        ,('2263', '25', 'RS-14-301', '60', '2016-03-01 13:45:49.767')
        ,('2263', '25', 'RS-14-298', '20', '2016-02-18 10:10:03.973')
        ,('2263', '25', 'RS-14-295', '1', '2016-02-11 17:04:54.423')
        ,('2263', '25', 'RS-14-294', '1', '2016-02-10 16:06:13.090')
        ,('2263', '25', 'RS-14-293', '2', '2016-02-10 15:58:40.353')
        ,('2263', '25', 'RS-14-276', '1', '2016-01-18 12:13:37.860')
        ,('2263', '25', 'RS-14-274', '2', '2016-01-14 14:33:53.863')
        ,('2263', '25', 'RS-14-271', '1', '2016-01-12 16:43:30.397')
        ,('2263', '25', 'RS-14-268', '1', '2016-01-11 16:43:36.843')
        ,('2263', '25', 'RS-14-267', '1', '2016-01-10 13:19:42.617')
        ,('2263', '25', 'RS-14-266', '1', '2016-01-06 15:58:00.513')
        ,('2263', '25', 'RS-14-259', '6', '2015-12-30 13:58:46.217')
        ,('2263', '25', 'RS-14-258', '1', '2015-12-30 10:59:23.120')
        ,('2263', '25', 'RS-14-250', '3', '2015-12-17 16:32:29.937')
        ,('2263', '25', 'RS-14-240', '1', '2015-12-06 13:13:45.847')
        ,('2263', '25', 'RS-14-236', '1', '2015-11-30 15:36:41.233')
        ,('2263', '25', 'RS-14-223', '2', '2015-11-10 13:04:17.540');

SELECT *
      ,SUM([Quantity]) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC)
      ,[GlobalBalance] - SUM([Quantity]) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC)
FROM @DataSource
ORDER BY [StockItemId]
        ,[Date] DESC;

WITH DataSource AS
(
    SELECT *
          ,[GlobalBalance] - SUM([Quantity]) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) AS [Dif]
          ,ROW_NUMBER() OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) [RecordID]
    FROM @DataSource
), DataSourceWithPrevDiff AS
(
    SELECT *
          ,LAG([Dif], 1, NULL) OVER (PARTITION BY [StockItemId], [GlobalBalance] ORDER BY [Date] DESC) AS [PrevDif]
    FROM DataSource
)
SELECT [StockItemId]
      ,[GlobalBalance]
      ,[Date]
      ,[Code]
      ,[Quantity]
     ,CASE WHEN [Dif] > 0 THEN [Quantity] 
           WHEN [RecordID] = 1 THEN [Quantity] + [Dif]
           WHEN [PrevDif] > 0 THEN [PrevDif]
      END AS [RS_Portion]
FROM DataSourceWithPrevDiff
WHERE [Dif] > 0 
    OR [PrevDif] > 0
    OR [RecordID] = 1
ORDER BY [StockItemId]
        ,[Date] DESC;