Postgres 中的库存计算

Posted

技术标签:

【中文标题】Postgres 中的库存计算【英文标题】:Stock calculation in Postgres 【发布时间】:2015-01-12 02:15:59 【问题描述】:

我有一张表p1,在 Postgres 中有这样的交易:

| id | product_id | transaction_date | quantity |
|----|------------|------------------|----------|
| 1  | 1          | 2015-01-01       | 1        |
| 2  | 1          | 2015-01-02       | 2        |
| 3  | 1          | 2015-01-03       | 3        |

p2 桌子上的产品如下:

| id | product      | stock |
|----|--------------|-------|
| 1  | Product A    | 15    |

p2' 中的stock 已针对p1 中的每条新记录而减少。

如何重建以前的状态来得到这个结果?

| product   | first_stock | quantity | last_stock |
|-----------|-------------|----------|------------|
| Product A | 21          | 1        | 20         |
| Product A | 20          | 2        | 18         |
| Product A | 18          | 3        | 15         |

我已经尝试使用lead() 来获取当前行之后的数量。

SELECT p2.product, p1.quantity, lead(p1.quantity) OVER(ORDER BY p1.id DESC)
FROM p1 INNER JOIN p2 ON p1.product_id = p2.id;

但是如何计算当前股票的领先行数?

【问题讨论】:

这个案子可以触发。 一开始没看懂这个问题,但仔细看后我想我明白了。修改了问题以解决不一致问题 - 我希望我理解正确。此外,简称为"Postgres", not "Postgre"。 当您收到的库存增加了手头库存时会发生什么?当然应该将它们考虑在内,不是吗? @Bohemian:p1.quantity 中的负值会这样做 - 除非我遗漏了什么? @ErwinBrandstetter 是的,我想是的。我假设(可能是错误的)transaction_date 列建议这些行用于“出售”,并且股票收购将记录在其他地方。但是,没有任何迹象表明这样的股票收购不可能是另一个“交易”,这样可以方便地更完整地解决这个问题,而无需额外的代码。 【参考方案1】:

您不仅需要lead(),还需要中间所有行的运行总和,以从交易数据中重建先前的状态:

SELECT p2.product
     , p2.stock + px.sum_quantity            AS first_stock
     , px.quantity
     , p2.stock + px.sum_quantity - quantity AS last_stock
FROM   p2
JOIN (
   SELECT product_id, quantity, transaction_date
        , sum(quantity) OVER (PARTITION BY product_id
                              ORDER BY transaction_date DESC) AS sum_quantity
   FROM   p1
   ) px ON px.product_id = p2.id
ORDER  BY px.transaction_date;

假设transaction_date实际指示的事件进程。

使用聚合函数sum() 作为窗口聚合函数来获取运行总和。使用子查询,因为我们多次使用数量的运行总和 (sum_quantity)。 为last_stock减去当前行的quantity(冗余添加后)。

吹毛求疵

理论上,为窗框使用自定义框架定义以仅将数量相加到前面行会更便宜,因此我们不加减的数量当前行冗余。但实际上这更复杂,速度也快不了多少:

SELECT p2.id, p2.product, px.transaction_date  -- plus id and date for context
     , p2.stock + COALESCE(px.pre_sum_q + px.quantity, 0) AS first_stock
     , px.quantity
     , p2.stock + COALESCE(px.pre_sum_q, 0)               AS last_stock
FROM  p2
LEFT JOIN (
   SELECT id, product_id, transaction_date
        , quantity
        , sum(quantity) OVER (PARTITION BY product_id
                              ORDER BY transaction_date DESC
               ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS pre_sum_q
   FROM   p1
   ) px ON px.product_id = p2.id
ORDER  BY px.transaction_date, px.id;

此相关答案中框架定义的说明:

Grouping based on sequence of rows

同时,还演示了如何使用LEFT JOINCOALESCE 防止在p1 中没有任何 相关行的产品的行和空值,以及如果同一产品在同一天有多个交易,则排序顺序稳定。

仍然假设所有列都定义为 NOT NULL,或者您需要为具有 NULL 值的极端情况做更多的事情。

【讨论】:

非常感谢您如此迅速地回答我的问题。并感谢您的精彩解释。我会试着理解你的答案。谢谢。

以上是关于Postgres 中的库存计算的主要内容,如果未能解决你的问题,请参考以下文章

如何使用枢轴将行转换为postgres中的列?让我来计算一下

如何计算 Postgres 整数数据类型的位数?

无法执行 plpgsql/postgres 中的函数

在 postgres 表中计算不为空的 json 键-> 值

在 psql 中可见的 Postgres 表,而不是来自 jdbc

如何在带有 Postgres 的动态框架中使用窗口函数中的列值?