Postgres 中的库存计算
Stock calculation in Postgres
我有一个 table 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
table 产品如下:
| id | product | stock |
|----|--------------|-------|
| 1 | Product A | 15 |
stock
in p2
' 已针对 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;
但是如何计算当前股票的领先行数?
您不仅需要 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()
作为 window-聚合函数来获得 运行 总和。使用子查询,因为我们多次使用 运行 数量总和 (sum_quantity
)。
对于 last_stock
减去当前行的 quantity
(在冗余添加之后)。
吹毛求疵
理论上,为 window 框架使用自定义框架定义来仅将数量相加到 preceding 行会更便宜,因此我们不添加并多余地减去 current 行的数量。但这在现实中更复杂而且几乎没有更快:
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
<b>ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING</b>) 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
来防止没有 any 的产品丢失行和空值p1
中的相关行,如果同一产品在同一天有多个交易,则排序顺序稳定。
仍然假设所有列都被定义为 NOT NULL,或者您需要为具有 NULL 值的极端情况做更多的事情。
我有一个 table 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
table 产品如下:
| id | product | stock |
|----|--------------|-------|
| 1 | Product A | 15 |
stock
in p2
' 已针对 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;
但是如何计算当前股票的领先行数?
您不仅需要 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()
作为 window-聚合函数来获得 运行 总和。使用子查询,因为我们多次使用 运行 数量总和 (sum_quantity
)。
对于 last_stock
减去当前行的 quantity
(在冗余添加之后)。
吹毛求疵
理论上,为 window 框架使用自定义框架定义来仅将数量相加到 preceding 行会更便宜,因此我们不添加并多余地减去 current 行的数量。但这在现实中更复杂而且几乎没有更快:
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
<b>ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING</b>) 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
来防止没有 any 的产品丢失行和空值p1
中的相关行,如果同一产品在同一天有多个交易,则排序顺序稳定。
仍然假设所有列都被定义为 NOT NULL,或者您需要为具有 NULL 值的极端情况做更多的事情。