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 JOIN
和COALESCE
防止在p1
中没有任何 相关行的产品的行和空值,以及如果同一产品在同一天有多个交易,则排序顺序稳定。
仍然假设所有列都定义为 NOT NULL,或者您需要为具有 NULL 值的极端情况做更多的事情。
【讨论】:
非常感谢您如此迅速地回答我的问题。并感谢您的精彩解释。我会试着理解你的答案。谢谢。以上是关于Postgres 中的库存计算的主要内容,如果未能解决你的问题,请参考以下文章
如何使用枢轴将行转换为postgres中的列?让我来计算一下
在 postgres 表中计算不为空的 json 键-> 值