使用递归CTE运行库存的平均值

时间:2018-01-10 12:45:50

标签: sql-server tsql sql-server-2012

我有以下数据,需要使用前面每行的数量来计算每行的运行平均值。

Option Explicit

Dim BigOne As BigClass

Private Sub Class_Initialize()
    Set BigOne = New BigClass
End Sub

Private Sub Class_Terminate()
    Set BigOne = Nothing
End Sub

Property Let var3(v As Integer)
    BigOne.var3 = v
End Property

Property Get var3() As Integer
    var3 = BigOne.var3
End Property

预期结果

CREATE TABLE [dbo].[AKTest](
[IntakeSellingPrice] [decimal](38, 20) NULL,
[IntakeSellingAmount] [decimal](38, 6) NULL,
[Item No_] [nvarchar](20) NOT NULL,
[Variant Code] [nvarchar](10) NOT NULL,
[Unit of Measure Code] [nvarchar](10) NOT NULL,
[Posting Date] [datetime] NOT NULL,
[PurchaseQty] [decimal](38, 20) NULL,
[ReceiptNo] [bigint] NULL,
[InventoryBalance] [decimal](38, 20) NOT NULL,
[NewBalance] [decimal](38, 20) NULL
) ON [PRIMARY]

GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(10.00000000000000000000 AS Decimal(38, 20)), CAST(1000.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80800000000 AS DateTime), CAST(100.00000000000000000000 AS Decimal(38, 20)), 1, CAST(0.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(5.00000000000000000000 AS Decimal(38, 20)), CAST(250.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80E00000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 2, CAST(50.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(12.50000000000000000000 AS Decimal(38, 20)), CAST(625.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A81900000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 3, CAST(60.00000000000000000000 AS Decimal(38, 20)), CAST(110.00000000000000000000 AS Decimal(38, 20)))
GO

我以前手动计算的Formular定义如下第三行。如果你先从底部开始,计算会更好。

A)我从使用receiptNo 3的底部开始,其中NewBalance是110。

B)购买50个单位,12.50 = 625

离开60个单位的C)在前一行中购买50个单位,用于5 = 250

D)留下10个单位。在上一行中购买100个单位,10 = 1000.但我们只需要10的成本,所以1000/10 = 100。

E)将所有费用加起来625 + 250 + 100 = 975/110 = 8.86

1 个答案:

答案 0 :(得分:2)

我不认为使用ROWS BETWEENOVER这是可能的,因为逻辑有点奇怪?

我创建了一个临时表来播放数据并测试结果,但基本上这只是添加了递归CTE的原始脚本:

CREATE TABLE #AKTest (
    [IntakeSellingPrice] [decimal](38, 20) NULL,
    [IntakeSellingAmount] [decimal](38, 6) NULL,
    [Item No_] [nvarchar](20) NOT NULL,
    [Variant Code] [nvarchar](10) NOT NULL,
    [Unit of Measure Code] [nvarchar](10) NOT NULL,
    [Posting Date] [datetime] NOT NULL,
    [PurchaseQty] [decimal](38, 20) NULL,
    [ReceiptNo] [bigint] NULL,
    [InventoryBalance] [decimal](38, 20) NOT NULL,
    [NewBalance] [decimal](38, 20) NULL);
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(10.00000000000000000000 AS Decimal(38, 20)), CAST(1000.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80800000000 AS DateTime), CAST(100.00000000000000000000 AS Decimal(38, 20)), 1, CAST(0.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(5.00000000000000000000 AS Decimal(38, 20)), CAST(250.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80E00000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 2, CAST(50.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(12.50000000000000000000 AS Decimal(38, 20)), CAST(625.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A81900000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 3, CAST(60.00000000000000000000 AS Decimal(38, 20)), CAST(110.00000000000000000000 AS Decimal(38, 20)))
GO
SELECT * FROM #AKTest;

WITH cte AS (
    SELECT
        ReceiptNo,
        ReceiptNo AS linked_to,
        NewBalance,
        NewBalance - PurchaseQty AS remaining,
        PurchaseQty AS purchased,
        IntakeSellingPrice
    FROM
        #AKTest
    UNION ALL
    SELECT
        c.ReceiptNo,
        c.linked_to - 1 AS linked_to,
        a.NewBalance,
        c.remaining - a.PurchaseQty AS remaining,
        CASE WHEN a.PurchaseQty > c.remaining THEN c.remaining ELSE a.PurchaseQty END AS purchased,
        a.IntakeSellingPrice
    FROM
        cte c
        INNER JOIN #AKTest a ON a.ReceiptNo = c.linked_to - 1
    WHERE
        c.linked_to > 1)
SELECT
    ReceiptNo,
    SUM(purchased * IntakeSellingPrice) / MAX(NewBalance) AS avg_price
FROM
    cte
GROUP BY
    ReceiptNo
ORDER BY
    ReceiptNo;

获取正确答案:

ReceiptNo   avg_price
1           10.000000
2           7.500000
3           8.863636

根据要求,这将显示表格中的所有数据,最终的平均价格为:

WITH cte AS (
    SELECT
        ReceiptNo,
        ReceiptNo AS linked_to,
        NewBalance,
        NewBalance - PurchaseQty AS remaining,
        PurchaseQty AS purchased,
        IntakeSellingPrice
    FROM
        #AKTest
    UNION ALL
    SELECT
        c.ReceiptNo,
        c.linked_to - 1 AS linked_to,
        a.NewBalance,
        c.remaining - a.PurchaseQty AS remaining,
        CASE WHEN a.PurchaseQty > c.remaining THEN c.remaining ELSE a.PurchaseQty END AS purchased,
        a.IntakeSellingPrice
    FROM
        cte c
        INNER JOIN #AKTest a ON a.ReceiptNo = c.linked_to - 1
    WHERE
        c.linked_to > 1),
Averages AS (
    SELECT
        ReceiptNo,
        SUM(purchased * IntakeSellingPrice) / MAX(NewBalance) AS avg_price
    FROM
        cte 
    GROUP BY
        ReceiptNo)
SELECT
    a.*,
    v.avg_price
FROM
    Averages v
    INNER JOIN #AKTest a ON a.ReceiptNo = v.ReceiptNo
ORDER BY
    a.ReceiptNo;