SQL - 计算产品的累积平均成本价

Posted

技术标签:

【中文标题】SQL - 计算产品的累积平均成本价【英文标题】:SQL - Calculate cumulative average cost price for a product 【发布时间】:2018-07-30 12:05:47 【问题描述】:

我想知道是否有人至少可以为我指出这个问题的正确方向。

我需要计算平均库存成本,但需要注意的是,它应该只根据我们库存的最新产品采购来计算。

为了更好地解释产品 x,我们以不同的成本从不同的供应商处购买了 1,200 件产品。我们只剩下 150 件库存,因此,要获得剩余商品的平均购买价格和价值,我们需要从最近购买的 150 件中获取产品的平均成本。

我有一个产生以下结果的视图:

 ProductId (int) - PK
 ProductName (varchar(150))
 StockInHand (int)

相关表格如下(为方便使用截断)

采购订单

PurchaseOrderId (int) - PK
PurchaseOrderDate (DateTime)
PurchaseOrderStatus (int) - FK

采购订单状态 - 1 = 打开,2 = 已签入

PurchaseOrderLine

PurchaseOrderLineId (int) - PK 
Qty (int)
UnitPrice (decimal(18,2))
ProductId (int) - FK
PurchaseOrderId (int) - FK

更新

我在这里创建了一个 sql-fiddle:

http://sqlfiddle.com/#!18/69288/1

你会看到我有三个商品采购订单:

1:数量 20 @ 40 英镑 2:数量 20 @ £30ea 3:数量 10 @ 25 英镑

我有两个相同商品的订单:

1:数量 10 2:数量 15

由此,我可以得到手头的库存:25

我需要剩余股票的平均购买价格。

我还剩 25 件,所以我需要从最新的采购订单开始逆向计算才能获得价值。

我将从采购订单 3 中取出 10 个,从采购订单 2 中取出 15 个:

10 * 25 英镑 = 250 英镑 15 * 30 英镑 = 450 英镑

700 英镑 / 25 = 28 英镑

我希望这能让我的问题更清楚!

谢谢

更新 2

非常感谢 Sticky Bit 抽出时间发布我的问题的解决方案并如此周到地解释它。

我已在我的开发数据库上尝试过此操作,但似乎遇到了问题。

我有 4 个同一产品的采购订单(其中两个是在同一天下的)

我还没有弄清楚如何在这个文本编辑器中优雅地格式化表格,所以请多多包涵:

PurchaseOrderId / PurchaseOrderDate 2 / 2018-07-28 3 / 2018-07-29 4 / 2018-07-30 5 / 2018-07-30

我有以下 PurchaseOrderLine

PurchaseOrderLineId / PurchaseOrderId / ProductId / 数量 / UnitPrice 3 / 2 / 8 / 20 / 400.00 4 / 3 / 8 / 40 / 420.00 5 / 4 / 8 / 25 / 500.00 6 / 5 / 8 / 1 / 200.00

运行以下:

SELECT pol.productid,
   po.purchaseorderdate,
   sum(pol.qty) qty,
   avg(pol.unitprice) unitprice
   FROM purchaseorder po
        INNER JOIN purchaseorderline pol
                   ON pol.purchaseorderid = po.purchaseorderid
   GROUP BY pol.productid,
            po.purchaseorderdate;

给我这些结果:

8 2018-07-28 00:00:00.000 20 400.000000 8 2018-07-29 00:00:00.000 40 420.000000 8 2018-07-30 00:00:00.000 26 350.000000

您会注意到,7 月 30 日购买的产品的平均成本有所下降(它采用两个价格之间的平均值,而不考虑数量 - 我不确定这是否是设计使然?)

如果我然后运行以下命令:

SELECT po.productid,
   po.purchaseorderdate,
   sum(po.qty) OVER (PARTITION BY po.productid
                     ORDER BY po.purchaseorderdate) qty,
   sum(po.unitprice) OVER (PARTITION BY po.productid
                           ORDER BY po.purchaseorderdate) unitprice
   FROM (SELECT pol.productid,
                po.purchaseorderdate,
                sum(pol.qty) qty,
                avg(pol.unitprice) unitprice
                FROM purchaseorder po
                     INNER JOIN purchaseorderline pol
                                ON pol.purchaseorderid = po.purchaseorderid
                GROUP BY pol.productid,
                         po.purchaseorderdate) po;

我得到以下结果:

8 2018-07-28 00:00:00.000 20 400.000000 8 2018-07-29 00:00:00.000 60 820.000000 8 2018-07-30 00:00:00.000 86 1170.000000

同样,这里的单价似乎有些不对劲。

非常感谢任何帮助!

亲切的问候

【问题讨论】:

示例数据(可能还有sql-fiddle 上的示例模式将有助于为这个问题创建答案,因为目前很难看出问题是什么是实时数量和价格的唯一链接在同一张桌子上 嗨 WTP,我创建了一个 sql-fiddle 并相应地更新了我的原始帖子。 那么unitprice 是什么?我想这是每件的价格,所以一条线的总价格是unitprice * qty。这个假设是否错误,unitprice 是整批的价格,即单个商品的价格是unitprice / qty?最后你想得到什么,每件的平均价格还是这个价格乘以库存商品的数量? 嗨 Sticky Bit,不,你是对的。单价是每件商品的价格。我需要一件物品的平均成本价,与我们剩余的库存量有关。例如,使用我上面更新的信息,如果我们有 30 件库存商品,平均成本价格将为 466 英镑:(1 x 200 = 200 英镑)+(25 x 500 = 12,500 英镑)+(4 x 420 = £1,280) / 30 如果这有点令人困惑,我深表歉意。再次感谢您 【参考方案1】:

    首先,我们需要找到一个查询来为我们提供每种产品的当前库存水平。为此,我们将 purchaseorderlineorderline 连接到 product 并计算每一行的差异。由于一个产品可以在多个订单中,我们另外汇总结果,以获得每个产品的总体差异 - 当前库存水平。

    SELECT p.productid,
           p.productname,
           sum(coalesce(pol.qty, 0) - coalesce(ol.qty, 0)) qty
           FROM product p
                LEFT JOIN purchaseorderline pol
                          ON pol.productid = p.productid
                LEFT JOIN orderline ol
                          ON ol.productid = p.productid
           GROUP BY p.productid,
                    p.productname;
    

    接下来,我们需要每个产品和日期的库存数量(purchaseorders)。为此,我们内部连接purchaseorderpurchaseorderline。我们再次汇总以考虑可能的情况,即在同一天为同一产品订购了多个订单。

    SELECT pol.productid,
           po.purchaseorderdate,
           sum(pol.qty) qty,
           sum(pol.qty * pol.unitprice) unitprice
           FROM purchaseorder po
                INNER JOIN purchaseorderline pol
                           ON pol.purchaseorderid = po.purchaseorderid
           GROUP BY pol.productid,
                    po.purchaseorderdate;
    

    我们现在可以使用之前的结果和窗口函数来获得每天的库存数量和产品平均价格的总和。

    SELECT po.productid,
           po.purchaseorderdate,
           sum(po.qty) OVER (PARTITION BY po.productid
                             ORDER BY po.purchaseorderdate) qty,
           sum(po.unitprice) OVER (PARTITION BY po.productid
                                   ORDER BY po.purchaseorderdate)
           /
           sum(po.qty) OVER (PARTITION BY po.productid
                             ORDER BY po.purchaseorderdate) unitprice
           FROM (SELECT pol.productid,
                        po.purchaseorderdate,
                        sum(pol.qty) qty,
                        sum(pol.qty * pol.unitprice) unitprice
                        FROM purchaseorder po
                             INNER JOIN purchaseorderline pol
                                        ON pol.purchaseorderid = po.purchaseorderid
                        GROUP BY pol.productid,
                                 po.purchaseorderdate) po;
    

现在我们使用OUTER APPLY 将 1. 和 2. 的结果放在一起。对于每种产品,我们从 2 中选择TOP 1 结果。按天降序排列(即年轻订单优先),库存数量大于或等于当前库存数量。

SELECT p.productid,
       p.productname,
       po.unitprice
       FROM (SELECT p.productid,
                    p.productname,
                    sum(coalesce(pol.qty, 0) - coalesce(ol.qty, 0)) qty
                    FROM product p
                         LEFT JOIN purchaseorderline pol
                                   ON pol.productid = p.productid
                         LEFT JOIN orderline ol
                                   ON ol.productid = p.productid
                    GROUP BY p.productid,
                             p.productname) p
            OUTER APPLY (SELECT TOP 1
                                po.unitprice
                                FROM (SELECT po.productid,
                                             po.purchaseorderdate,
                                             sum(po.qty) OVER (PARTITION BY po.productid
                                                               ORDER BY po.purchaseorderdate) qty,
                                             sum(po.unitprice) OVER (PARTITION BY po.productid
                                                                     ORDER BY po.purchaseorderdate)
                                             /
                                             sum(po.qty) OVER (PARTITION BY po.productid
                                                               ORDER BY po.purchaseorderdate) unitprice
                                             FROM (SELECT pol.productid,
                                                          po.purchaseorderdate,
                                                          sum(pol.qty) qty,
                                                          sum(pol.qty * pol.unitprice) unitprice
                                                          FROM purchaseorder po
                                                               INNER JOIN purchaseorderline pol
                                                                          ON pol.purchaseorderid = po.purchaseorderid
                                                          GROUP BY pol.productid,
                                                                   po.purchaseorderdate) po) po
                                WHERE po.productid = p.productid
                                      AND po.qty >= p.qty
                                ORDER BY po.purchaseorderdate DESC) po;

【讨论】:

亲爱的 Sticky Bit,非常感谢您抽出时间帮助我解决这个问题。非常感谢。我已经更新了我的帖子,以显示我在您的解决方案中遇到的问题。再次感谢您 @GrantS:我编辑了我的答案。我希望我这次做对了?如果没有,我建议您添加一个更全面的数据集(到您的小提琴中)和(部分)结果,这适合它。 亲爱的 Sticky Bit,这看起来在计算平均价格方面非常有效。非常感谢你做的这些。唯一的问题是最终结果显示了我所有具有相同“单价”的产品的列表。我只有一种产品的购买记录,所以这些值应该是“空”吗?再次感谢您 亲爱的 Sticky Bit,我添加了以下内容: WHERE po.qty >= p.qty AND po.ProductId = p.ProductId 这似乎使它完美运行!然而,还有一件事。我有一个带有 ProductId、VariationName 的 ProductVariation 表。产品可以有零个或多个变体,我需要使用您提供的解决方案来解决这个问题。我应该将此标记为解决方案并创建一个更有条理的问题吗?我以为我可以自己推断解决方案,但我遇到了麻烦。亲切的问候, @GrantS:你说得对,我忘了链接productid。我纠正了这一点。【参考方案2】:

我没有看到“UnitCost”,因此我将在代码示例中使用“UnitPrice”作为成本。

我不知道这是否完美,但它可能会帮助您朝着正确的方向前进。

SELECT (SUM(UnitPrice)/150) FROM OrderDetails
WHERE PurchaseOrderId IS BETWEEN 1050 AND 1200
GO

【讨论】:

您好 Jonah,非常感谢您抽出宝贵时间回复。您的解决方案并不是我真正想要的。我已经更新了我的原始帖子以更好地解释我的问题。再次感谢。

以上是关于SQL - 计算产品的累积平均成本价的主要内容,如果未能解决你的问题,请参考以下文章

永久加权平均成本计算 SQL Server 2008

触发器上的变异错误以计算平均成本

计算累积平均值(平均值)

如何计算累积平均收入? Python

MATLAB 中忽略 NaN 的累积行值的平均计算

SQL:时间维度和计算平均值